diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..2d42e20 --- /dev/null +++ b/src/main.py @@ -0,0 +1,7 @@ +from ui.app import AppUI + + +if __name__ == "__main__": + # start the ui + app = AppUI() + app.run() \ No newline at end of file diff --git a/src/ui/app.py b/src/ui/app.py new file mode 100644 index 0000000..122fcd9 --- /dev/null +++ b/src/ui/app.py @@ -0,0 +1,12 @@ +from textual.app import App, ComposeResult +from textual.widgets import Footer + +from ui.widgets.sidebar import Sidebar +from ui.widgets.timeline import Timeline + + +class AppUI(App): + def compose(self) -> ComposeResult: + yield Sidebar() + yield Timeline() + yield Footer() \ No newline at end of file diff --git a/src/ui/widgets/channel.py b/src/ui/widgets/channel.py new file mode 100644 index 0000000..ef75094 --- /dev/null +++ b/src/ui/widgets/channel.py @@ -0,0 +1,68 @@ +from textual.containers import VerticalGroup, Horizontal +from textual.app import ComposeResult +from textual.widgets import Checkbox, Input, Static + +from textual_slider import Slider + + +class Channel(VerticalGroup): + DEFAULT_CSS = """ + Channel { + height: 8; + border: tall $surface-lighten-1; + background: $surface-darken-1; + padding: 0 1; + margin-bottom: 1; + + #name { + padding: 0 1; + margin-bottom: 1; + } + + Checkbox { + margin-right: 1; + } + + .channel-slider { + height: 1; + Static { + width: 50%; + margin-right: 1; + } + + Slider { + width: 45%; + border: none; + max-height: 1; + } + + } + + #channel-buttons { + margin-top: 1; + } + } + """ + + def __init__(self, channel_name: str = "", muted: bool = False, solo: bool = False, pan: float = 0, volume: float = 0): + super().__init__() + self.channel_name = channel_name + self.muted = muted + self.solo = solo + self.pan = pan + self.volume = volume + + def compose(self) -> ComposeResult: + yield Input(placeholder="Channel Name", compact=True, id="name", value=self.channel_name) + + with Horizontal(classes="channel-slider"): + yield Static("Pan (center):") + yield Slider(-100, 100, step=1, value=self.pan) + with Horizontal(classes="channel-slider"): + yield Static("Volume (+ 0Db):") + yield Slider(-15, 15, step=0.1, value=self.volume) + + with Horizontal(id="channel-buttons"): + yield Checkbox("Mute", compact=True, id="mute", tooltip="Mute this track", value=self.muted) + yield Checkbox("Solo", compact=True, id="solo", tooltip="Hear only this track", value=self.solo) + \ No newline at end of file diff --git a/src/ui/widgets/sidebar.py b/src/ui/widgets/sidebar.py new file mode 100644 index 0000000..a74974a --- /dev/null +++ b/src/ui/widgets/sidebar.py @@ -0,0 +1,28 @@ +from textual.containers import Vertical, VerticalScroll, Horizontal +from textual.widgets import Button, ListView +from textual.app import ComposeResult + +from ui.widgets.channel import Channel + + +class Sidebar(Vertical): + DEFAULT_CSS = """ + Sidebar { + dock: left; + background: $surface; + width: 40; + border-right: tall $surface-lighten-1; + padding: 1; + + #add-channel { + min-width: 100%; + margin: 1 + } + } + """ + + def compose(self) -> ComposeResult: + with VerticalScroll(id="channels"): + yield Channel() + yield Channel() + yield Button("+ New Channel", variant="success", id="add-channel") \ No newline at end of file diff --git a/src/ui/widgets/timeline.py b/src/ui/widgets/timeline.py new file mode 100644 index 0000000..7cb47b7 --- /dev/null +++ b/src/ui/widgets/timeline.py @@ -0,0 +1,27 @@ +from textual.containers import VerticalScroll, Horizontal +from textual.app import ComposeResult + + +class TimelineRow(Horizontal): + DEFAULT_CSS = """ + TimelineRow { + background: $surface-lighten-1; + height: 8; + margin-bottom: 1; + } + """ + + def compose(self) -> ComposeResult: + yield from () + +class Timeline(VerticalScroll): + DEFAULT_CSS = """ + Timeline { + padding: 1 0; + hatch: "-" $surface-lighten-1; + } + """ + + def compose(self) -> ComposeResult: + yield TimelineRow() + yield TimelineRow() \ No newline at end of file