you can drag things left and right and the mute and solo buttons update the ui now
This commit is contained in:
@@ -18,7 +18,8 @@ class SongPlayer:
|
|||||||
self.timer = Timer(
|
self.timer = Timer(
|
||||||
timeline,
|
timeline,
|
||||||
0.1,
|
0.1,
|
||||||
pause=True
|
pause=True,
|
||||||
|
skip=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def play_callback(self, out, frames, time, status):
|
def play_callback(self, out, frames, time, status):
|
||||||
|
|||||||
Binary file not shown.
@@ -100,7 +100,27 @@ class AppUI(App):
|
|||||||
|
|
||||||
self.open_project_path = path
|
self.open_project_path = path
|
||||||
|
|
||||||
### load all the ui elements
|
### load all the ui element
|
||||||
|
|
||||||
|
# timeline tracks
|
||||||
|
timeline = self.query_one(Timeline)
|
||||||
|
rows = timeline.query_one("#rows")
|
||||||
|
|
||||||
|
for i, channel in enumerate(self.project.channels):
|
||||||
|
row = TimelineRow(channel, id=f"row-{i}")
|
||||||
|
|
||||||
|
|
||||||
|
row.styles.width = timeline.bar_offset * self.project.song_length
|
||||||
|
rows.mount(row)
|
||||||
|
|
||||||
|
for chunk in channel.chunks:
|
||||||
|
|
||||||
|
if chunk.chunk_type == ChunkType.CHUNK:
|
||||||
|
row.mount(Chunk(chunk, chunk_name=chunk.name, bar_pos=chunk.position))
|
||||||
|
elif chunk.chunk_type == ChunkType.AUDIO:
|
||||||
|
row.mount(AudioChunk(chunk, chunk.name, chunk.position))
|
||||||
|
|
||||||
|
|
||||||
# sidebar channels
|
# sidebar channels
|
||||||
sidebar_channels = self.query_one("#channels")
|
sidebar_channels = self.query_one("#channels")
|
||||||
|
|
||||||
@@ -121,25 +141,6 @@ class AppUI(App):
|
|||||||
channel.volume
|
channel.volume
|
||||||
), before=-1)
|
), before=-1)
|
||||||
|
|
||||||
# timeline tracks
|
|
||||||
timeline = self.query_one(Timeline)
|
|
||||||
rows = timeline.query_one("#rows")
|
|
||||||
|
|
||||||
for channel in self.project.channels:
|
|
||||||
row = TimelineRow(channel)
|
|
||||||
|
|
||||||
|
|
||||||
row.styles.width = timeline.bar_offset * self.project.song_length
|
|
||||||
rows.mount(row)
|
|
||||||
|
|
||||||
for chunk in channel.chunks:
|
|
||||||
|
|
||||||
if chunk.chunk_type == ChunkType.CHUNK:
|
|
||||||
row.mount(Chunk(chunk_name=chunk.name, bar_pos=chunk.position))
|
|
||||||
elif chunk.chunk_type == ChunkType.AUDIO:
|
|
||||||
row.mount(AudioChunk(chunk.audio_data, chunk.sample_rate, chunk.name, chunk.position))
|
|
||||||
|
|
||||||
|
|
||||||
self.notify(f"Loaded \"{path}\"")
|
self.notify(f"Loaded \"{path}\"")
|
||||||
|
|
||||||
self.push_screen(FileOpen(
|
self.push_screen(FileOpen(
|
||||||
@@ -163,10 +164,12 @@ class AppUI(App):
|
|||||||
case _:
|
case _:
|
||||||
self.notify("Sorry, that isn't implemented yet... ;-;", severity="warning")
|
self.notify("Sorry, that isn't implemented yet... ;-;", severity="warning")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@on(Tabs.TabActivated)
|
@on(Tabs.TabActivated)
|
||||||
async def top_menu_tab_clicked(self, event: Tabs.TabActivated):
|
async def top_menu_tab_clicked(self, event: Tabs.TabActivated):
|
||||||
|
event.tabs.active = None
|
||||||
if self.first_tab_click:
|
if self.first_tab_click:
|
||||||
event.tabs.active = None
|
|
||||||
self.first_tab_click = False
|
self.first_tab_click = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class SettingsScreen(ModalScreen):
|
|||||||
with HorizontalGroup(classes="setting"):
|
with HorizontalGroup(classes="setting"):
|
||||||
with VerticalGroup():
|
with VerticalGroup():
|
||||||
yield Label("Colour Theme", classes="setting-name")
|
yield Label("Colour Theme", classes="setting-name")
|
||||||
yield Label("Colour theme used for the entire Berry app. You can get more themes with plugins!", classes="setting-desc")
|
yield Label("Colour theme used for the entire app.", classes="setting-desc")
|
||||||
|
|
||||||
yield Select.from_values(
|
yield Select.from_values(
|
||||||
(theme_name for theme_name in self.app._registered_themes.keys() if theme_name != "textual-ansi"),
|
(theme_name for theme_name in self.app._registered_themes.keys() if theme_name != "textual-ansi"),
|
||||||
|
|||||||
@@ -57,15 +57,36 @@ class Channel(VerticalGroup):
|
|||||||
self.solo = solo
|
self.solo = solo
|
||||||
self.pan = pan
|
self.pan = pan
|
||||||
self.volume = volume
|
self.volume = volume
|
||||||
print(self.channel_name)
|
print("hi")
|
||||||
|
print(self.timeline_row)
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
if self.muted:
|
||||||
|
self.timeline_row.add_class("-muted")
|
||||||
|
if self.solo:
|
||||||
|
self.timeline_row.add_class("-solo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def timeline_row(self):
|
||||||
|
return self.app.query_one(f"#row-{self.channel_index}")
|
||||||
|
|
||||||
def on_checkbox_changed(self, event: Checkbox.Changed):
|
def on_checkbox_changed(self, event: Checkbox.Changed):
|
||||||
if event.checkbox.id == "mute":
|
if event.checkbox.id == "mute":
|
||||||
self.muted = event.value
|
self.muted = event.value
|
||||||
|
self.project_channel.mute = self.muted
|
||||||
|
|
||||||
self.app.query_one("#timeline").query_one()
|
if self.muted:
|
||||||
|
self.timeline_row.add_class("-muted")
|
||||||
|
else:
|
||||||
|
self.timeline_row.remove_class("-muted")
|
||||||
elif event.checkbox.id == "solo":
|
elif event.checkbox.id == "solo":
|
||||||
self.solo = event.value
|
self.solo = event.value
|
||||||
|
self.project_channel.solo = self.solo
|
||||||
|
|
||||||
|
if self.solo:
|
||||||
|
self.timeline_row.add_class("-solo")
|
||||||
|
else:
|
||||||
|
self.timeline_row.remove_class("-solo")
|
||||||
|
|
||||||
def on_slider_changed(self, event: Slider.Changed):
|
def on_slider_changed(self, event: Slider.Changed):
|
||||||
if event.slider.id == "volume":
|
if event.slider.id == "volume":
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ class AudioChunk(Chunk):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, audio_data: np.ndarray, sample_rate: int, chunk_name: str = "Sample", bar_pos: float = 0.0):
|
def __init__(self, project_chunk, chunk_name: str = "Sample", bar_pos: float = 0.0):
|
||||||
super().__init__(chunk_name, bar_pos)
|
super().__init__(project_chunk, chunk_name, bar_pos)
|
||||||
|
|
||||||
self.audio = audio_data
|
self.audio = project_chunk.audio_data
|
||||||
self.sample_rate = sample_rate
|
self.sample_rate = project_chunk.sample_rate
|
||||||
|
|
||||||
self.num_channels = None
|
self.num_channels = None
|
||||||
if len(self.audio.shape) == 1:
|
if len(self.audio.shape) == 1:
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
from textual.containers import Container
|
from textual.containers import Container
|
||||||
|
from textual import on, events
|
||||||
|
from textual_window import Window
|
||||||
|
from textual.widgets import Button
|
||||||
|
|
||||||
|
from project import ChannelChunk
|
||||||
|
|
||||||
|
|
||||||
class Chunk(Container):
|
class Chunk(Container):
|
||||||
@@ -11,16 +16,31 @@ class Chunk(Container):
|
|||||||
border: tab $surface;
|
border: tab $surface;
|
||||||
background: $surface-darken-1;
|
background: $surface-darken-1;
|
||||||
dock: left;
|
dock: left;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-tint: $primary 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
background: $surface-lighten-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, chunk_name: str = "Chunk", bar_pos: float = 0.0):
|
can_focus = True
|
||||||
|
|
||||||
|
def __init__(self, project_chunk: ChannelChunk, chunk_name: str = "Chunk", bar_pos: float = 0.0):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.project_chunk = project_chunk
|
||||||
self.chunk_name = chunk_name
|
self.chunk_name = chunk_name
|
||||||
self.border_title = chunk_name
|
self.border_title = chunk_name
|
||||||
self.bar_pos = bar_pos
|
self.bar_pos = bar_pos
|
||||||
self.update_offset()
|
self.update_offset()
|
||||||
|
|
||||||
|
self.being_dragged = False
|
||||||
|
|
||||||
def update_offset(self, timeline=None):
|
def update_offset(self, timeline=None):
|
||||||
if timeline == None:
|
if timeline == None:
|
||||||
timeline = self.app.query_one("#timeline")
|
timeline = self.app.query_one("#timeline")
|
||||||
@@ -28,3 +48,33 @@ class Chunk(Container):
|
|||||||
|
|
||||||
def calculate_size(self):
|
def calculate_size(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@on(events.MouseMove)
|
||||||
|
def continue_drag(self, event: events.MouseUp):
|
||||||
|
if not self.being_dragged: return
|
||||||
|
|
||||||
|
# move the widget in the ui
|
||||||
|
offset_x = self.styles.offset.x.cells + event.delta_x
|
||||||
|
self.styles.offset = (offset_x, 0)
|
||||||
|
|
||||||
|
# update the backend as well
|
||||||
|
timeline = self.app.query_one("#timeline")
|
||||||
|
pos = offset_x / (timeline.bar_offset * 4)
|
||||||
|
|
||||||
|
self.bar_pos = pos
|
||||||
|
self.project_chunk.position = pos
|
||||||
|
|
||||||
|
@on(events.MouseUp)
|
||||||
|
def end_drag(self, event: events.MouseUp):
|
||||||
|
if event.button == 1:
|
||||||
|
self.being_dragged = False
|
||||||
|
self.remove_class("dragging")
|
||||||
|
self.release_mouse()
|
||||||
|
|
||||||
|
@on(events.MouseDown)
|
||||||
|
def start_drag(self, event: events.MouseDown):
|
||||||
|
if event.button == 1:
|
||||||
|
self.being_dragged = True
|
||||||
|
self.add_class("dragging")
|
||||||
|
self.capture_mouse()
|
||||||
|
self.focus()
|
||||||
@@ -29,14 +29,4 @@ class Sidebar(Vertical):
|
|||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
with VerticalScroll(id="channels"):
|
with VerticalScroll(id="channels"):
|
||||||
for i, channel in enumerate(self.app.project.channels):
|
|
||||||
yield Channel(
|
|
||||||
i,
|
|
||||||
channel,
|
|
||||||
channel.name,
|
|
||||||
channel.mute,
|
|
||||||
channel.solo,
|
|
||||||
channel.pan,
|
|
||||||
channel.volume
|
|
||||||
)
|
|
||||||
yield Button("+ New Channel", variant="success", id="add-channel")
|
yield Button("+ New Channel", variant="success", id="add-channel")
|
||||||
@@ -18,17 +18,17 @@ class TimelineRow(Horizontal):
|
|||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
|
|
||||||
&.-muted {
|
&.-muted {
|
||||||
background: $error 25%;
|
background-tint: $background-darken-2 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-solo {
|
&.-solo {
|
||||||
background: $warning 25%;
|
background-tint: $warning 20%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_channel):
|
def __init__(self, project_channel, id: str | None = None):
|
||||||
super().__init__()
|
super().__init__(id=id)
|
||||||
self.project_channel = project_channel
|
self.project_channel = project_channel
|
||||||
|
|
||||||
class Timeline(Vertical):
|
class Timeline(Vertical):
|
||||||
@@ -139,18 +139,6 @@ class Timeline(Vertical):
|
|||||||
|
|
||||||
|
|
||||||
with VerticalScroll(id="rows"):
|
with VerticalScroll(id="rows"):
|
||||||
for channel in self.app.project.channels:
|
|
||||||
with TimelineRow(channel) as row:
|
|
||||||
|
|
||||||
row.styles.width = self.bar_offset * self.app.project.song_length
|
|
||||||
|
|
||||||
for chunk in channel.chunks:
|
|
||||||
|
|
||||||
if chunk.chunk_type == ChunkType.CHUNK:
|
|
||||||
yield Chunk(chunk_name=chunk.name, bar_pos=chunk.position)
|
|
||||||
elif chunk.chunk_type == ChunkType.AUDIO:
|
|
||||||
yield AudioChunk(chunk.audio_data, chunk.sample_rate, chunk.name, chunk.position)
|
|
||||||
|
|
||||||
for i in range(1, self.app.project.song_length+1):
|
for i in range(1, self.app.project.song_length+1):
|
||||||
bar = None
|
bar = None
|
||||||
if i % 4 == 0:
|
if i % 4 == 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user