Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod side_panel;
pub mod slot_manager;
pub mod sound_editor;
pub mod sound_grid;
pub mod sound_tile;
pub mod theme;
pub mod volume;
pub mod waveform;
Expand Down
140 changes: 32 additions & 108 deletions src/ui/sound_grid.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use iced::widget::{button, column, container, mouse_area, row, text};
use iced::{Border, Element, Length};
use iced::{mouse, Element, Length};

use crate::app::Message;
use crate::state::{SlotMap, SoundEntry, SoundMetaStore};
use crate::ui::theme::{self, Hh, Theme, Tone};
use crate::ui::sound_tile::{self, SoundTileData};
use crate::ui::theme::{self, Hh, Theme};

#[derive(Clone, Copy)]
pub struct SlotCtx<'a> {
Expand Down Expand Up @@ -61,14 +62,11 @@ pub fn view_grid<'a>(
.iter()
.map(|sound| {
let is_playing = playing == Some(sound.id.as_str());
let tone_idx = sound
.id
.get(..8)
.and_then(|s| u64::from_str_radix(s, 16).ok())
.unwrap_or(0) as usize;
let tile = tile_view(sound, is_playing, Tone::from_index(tone_idx), theme, ctx);
let tile = sound_tile::view(tile_data(sound, ctx), theme, is_playing);
mouse_area(tile)
.on_press(Message::PlaySound(sound.id.clone()))
.on_right_press(Message::OpenContextMenu(sound.id.clone()))
.interaction(mouse::Interaction::Pointer)
.into()
})
.collect();
Expand All @@ -88,103 +86,38 @@ pub fn view_grid<'a>(
grid.width(Length::Fill).into()
}

fn tile_view<'a>(
sound: &'a SoundEntry,
is_playing: bool,
tone: Tone,
theme: Theme,
ctx: TileCtx<'a>,
) -> Element<'a, Message> {
let duration_str = match sound.duration_ms {
Some(ms) => {
let secs = ms / 1000;
format!("{}:{:02}", secs / 60, secs % 60)
}
None => "\u{2014}".into(),
};

let is_fav = ctx.sound_meta.is_favorite(&sound.id);
let name_str = ctx
fn tile_data(sound: &SoundEntry, ctx: TileCtx<'_>) -> SoundTileData {
let seed = sound_tile::seed_from_sound_id(&sound.id);
let name = ctx
.sound_meta
.get_ref(&sound.id)
.and_then(|m| m.display_name.as_deref())
.unwrap_or(sound.name.as_str());

let category_text = text(sound.category.clone())
.size(theme::font::LABEL)
.color(theme.ink_dim());

let name_label = if is_fav {
format!("\u{2605} {name_str}")
} else {
name_str.to_owned()
};
let name_text = text(name_label).size(theme::font::BODY).color(theme.ink());
let duration_text = text(duration_str)
.size(theme::font::LABEL)
.color(theme.ink_faint());

let slot_badge: Option<Element<'_, Message>> = if ctx.shortcuts_active {
ctx.slot_ctx.slots.slot_for(&sound.path).and_then(|idx| {
ctx.slot_ctx
.triggers
.get(idx as usize)
.and_then(|t| t.as_deref())
.map(|trigger| {
container(
text(trigger)
.size(theme::font::LABEL)
.font(iced::Font::MONOSPACE)
.color(theme.ink_dim()),
)
.padding([2, 6])
.style(move |_t| container::Style {
background: Some(theme::bg_color(theme.panel())),
border: theme::tile_border(theme.hairline(), 1.0),
..Default::default()
})
.into()
})
})
} else {
None
};

let mut col = column![category_text, name_text, duration_text].spacing(theme::space::SM);
if let Some(badge) = slot_badge {
col = col.push(badge);
.unwrap_or(sound.name.as_str())
.to_owned();

SoundTileData {
id: sound.id.clone(),
name,
category: sound.category.clone(),
duration: crate::ui::fmt_duration(sound.duration_ms),
hotkey: hotkey_for(sound, ctx),
favorite: ctx.sound_meta.is_favorite(&sound.id),
tone: sound_tile::tone_from_seed(seed),
seed,
}
let content = col.padding(theme::space::LG);
}

let bg = tone.tile_tint(theme.is_dark());
let border_color = if is_playing {
theme.accent()
} else {
theme.hairline()
};
let border_width = if is_playing { 2.5 } else { 1.0 };
fn hotkey_for(sound: &SoundEntry, ctx: TileCtx<'_>) -> Option<String> {
if !ctx.shortcuts_active {
return None;
}

button(content)
.on_press(Message::PlaySound(sound.id.clone()))
.width(Length::Fill)
.height(theme::component::SOUND_TILE_H)
.style(move |_theme, status| {
let bg_final = match status {
button::Status::Hovered | button::Status::Pressed => lighten(bg, 0.03),
_ => bg,
};
button::Style {
background: Some(theme::bg_color(bg_final)),
text_color: theme.ink(),
border: Border {
color: border_color,
width: border_width,
radius: theme::radius::TILE,
},
..Default::default()
}
})
.into()
ctx.slot_ctx.slots.slot_for(&sound.path).and_then(|idx| {
ctx.slot_ctx
.triggers
.get(idx as usize)
.and_then(|trigger| trigger.clone())
})
}

// Width and estimated max height of the context menu popup.
Expand Down Expand Up @@ -322,12 +255,3 @@ pub fn context_menu_overlay<'a>(
.height(Length::Fill)
.into()
}

fn lighten(c: iced::Color, amount: f32) -> iced::Color {
iced::Color {
r: (c.r + amount).min(1.0),
g: (c.g + amount).min(1.0),
b: (c.b + amount).min(1.0),
a: c.a,
}
}
Loading