From 30835a506e49e4682bcb810ce107b7d1db48f5e0 Mon Sep 17 00:00:00 2001 From: SpookyDervish Date: Tue, 13 Jan 2026 21:33:25 +1100 Subject: [PATCH] implementing UI for bottom controls --- requirements.txt | 1 + src/ui/app.py | 2 ++ src/ui/widgets/chunk_types/audio.py | 14 +++++++-- src/ui/widgets/project_settings.py | 48 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/ui/widgets/project_settings.py diff --git a/requirements.txt b/requirements.txt index 7ebe0f2..7d1ca4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ pymp3 textual textual-slider +textual-plot numpy \ No newline at end of file diff --git a/src/ui/app.py b/src/ui/app.py index 7928f59..8e29267 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -3,6 +3,7 @@ from textual.widgets import Footer from ui.widgets.sidebar import Sidebar from ui.widgets.timeline import Timeline +from ui.widgets.project_settings import ProjectSettings class AppUI(App): @@ -13,4 +14,5 @@ class AppUI(App): def compose(self) -> ComposeResult: yield Sidebar() yield Timeline() + yield ProjectSettings() yield Footer() \ No newline at end of file diff --git a/src/ui/widgets/chunk_types/audio.py b/src/ui/widgets/chunk_types/audio.py index 57fec27..8806b1c 100644 --- a/src/ui/widgets/chunk_types/audio.py +++ b/src/ui/widgets/chunk_types/audio.py @@ -1,9 +1,11 @@ import librosa import pyloudnorm as pyln import math +import random from textual.containers import Vertical from textual.widgets import Sparkline +from textual_plot import HiResMode, PlotWidget from ui.widgets.chunk_types.chunk import Chunk @@ -12,7 +14,7 @@ from ui.widgets.chunk_types.chunk import Chunk class AudioChunk(Chunk): DEFAULT_CSS = """ AudioChunk { - + align: left middle; Sparkline { margin: 1; } @@ -41,6 +43,11 @@ class AudioChunk(Chunk): self.styles.width = (self.num_samples / self.sample_rate) / self.app.zoom_level + def on_mount(self): + for plot in self.query(PlotWidget): + plot.margin_top = 0 + plot.margin_left = 0 + plot.margin_bottom = 0 def compose(self) -> ComposeResult: @@ -56,6 +63,8 @@ class AudioChunk(Chunk): samples.append(self.audio[channel, sample]) yield Sparkline(data=samples) + + else: # just display the one channel samples = [] @@ -63,5 +72,4 @@ class AudioChunk(Chunk): for sample in range(0, self.num_samples, int(self.sample_rate*0.1)): samples.append(self.audio[sample]) - yield Sparkline(data=samples) - \ No newline at end of file + yield Sparkline(data=samples) \ No newline at end of file diff --git a/src/ui/widgets/project_settings.py b/src/ui/widgets/project_settings.py new file mode 100644 index 0000000..e3a9f98 --- /dev/null +++ b/src/ui/widgets/project_settings.py @@ -0,0 +1,48 @@ +from textual.containers import Horizontal +from textual.app import ComposeResult +from textual.widgets import Button, Input, Static + + +class ProjectSettings(Horizontal): + DEFAULT_CSS = """ + ProjectSettings { + height: 6; + margin-bottom: 1; + dock: bottom; + background: $surface-darken-1; + border-top: tall $surface-lighten-1; + align-vertical: middle; + padding: 0 1; + + Button { + max-width: 5; + } + + #song-bpm { + max-width: 12; + } + + #song-time-sig { + max-width: 16; + } + + Static { + height: 3; + content-align: left middle; + width: auto; + } + } + """ + + def __init__(self): + super().__init__() + self.border_title = "Project" + + def compose(self) -> ComposeResult: + yield Button("▶", tooltip="Play song", flat=True, id="play-button", variant="success") # icon becomes "⏸" when song is playing + + yield Static(" BPM: ") + yield Input("120", placeholder="120", valid_empty=False, type="integer", max_length=3, id="song-bpm") + + yield Static(" Time Signature: ") + yield Input("4/4", placeholder="4/4", valid_empty=False, id="song-time-sig") \ No newline at end of file