···
-
button, column, container, horizontal_rule, pane_grid, row, scrollable, text, text_input,
use iced::{Length, Padding, Theme};
···
panes: pane_grid::State<PaneContent>,
-
selected_channel: Option<usize>,
-
selected_tracks: Vec<usize>,
modifiers: iced::keyboard::Modifiers,
···
-
selected_channel: None,
-
selected_tracks: Vec::new(),
modifiers: iced::keyboard::Modifiers::default(),
palette: Palette::default(),
···
-
ChannelSelected(usize),
ModifiersChanged(iced::keyboard::Modifiers),
SearchChanged(pane_grid::Pane, String),
···
ViewEditor(view_editor::Message),
-
fn matches_query(text: &str, query: &str) -> bool {
-
text.to_lowercase().contains(&query.to_lowercase())
fn update(state: &mut State, message: Message) {
-
Message::ChannelSelected(idx) => {
-
state.selected_channel = Some(idx);
-
if let Some(channel) = state.channels.get(idx) {
-
let view = view::View::channel_tracks(&channel.slug, &channel.name);
-
let view_id = view.id.clone();
-
if !state.views.iter().any(|v| v.id == view_id) {
-
state.views.push(view);
-
if let Some((_pane_id, content)) = state.panes.iter_mut().find(|(_, c)| {
-
.map(|id| id != "all-channels")
-
content.push_view(view_id);
-
Message::TrackClicked(index) => {
-
let ctrl = state.modifiers.command() || state.modifiers.control();
-
let shift = state.modifiers.shift();
-
if state.selected_tracks.contains(&index) {
-
state.selected_tracks.retain(|&i| i != index);
-
state.selected_tracks.push(index);
-
if let Some(&last) = state.selected_tracks.last() {
-
let start = last.min(index);
-
let end = last.max(index);
-
state.selected_tracks.clear();
-
state.selected_tracks.extend(start..=end);
-
state.selected_tracks = vec![index];
-
state.selected_tracks = vec![index];
Message::ModifiersChanged(modifiers) => {
state.modifiers = modifiers;
Message::SearchChanged(pane, query) => {
if let Some(content) = state.panes.get(pane) {
···
let result_count = match view.source {
-
view::DataSource::Channels => filtered_channels(state, view).len(),
-
view::DataSource::Tracks => filtered_tracks(state, view).len(),
let search_placeholder = match view.source {
···
let view_content = match view.source {
-
view::DataSource::Channels => render_channels_view(view, state, palette),
-
view::DataSource::Tracks => render_tracks_view(view, state, palette),
column![tabs, search_bar, view_content]
···
-
fn filtered_channels<'a>(state: &'a State, view: &view::View) -> Vec<(usize, &'a Channel)> {
-
.filter(|(_idx, channel)| {
-
if let Some(ref slug) = view.filter.slug {
-
if &channel.slug != slug {
-
if let Some(min) = view.filter.track_count_min {
-
if channel.track_count < min {
-
if let Some(max) = view.filter.track_count_max {
-
if channel.track_count > max {
-
if !view.filter.search_query.is_empty()
-
&& !matches_query(&channel.name, &view.filter.search_query)
-
&& !matches_query(&channel.slug, &view.filter.search_query)
-
fn render_channels_view<'a>(
-
) -> iced::Element<'a, Message> {
-
let mut sorted_channels = filtered_channels(state, view);
-
match view.sort.field {
-
view::SortField::Name => {
-
sorted_channels.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name));
-
view::SortField::TrackCount => {
-
sorted_channels.sort_by_key(|(_, ch)| ch.track_count);
-
view::SortField::LatestTrack => {
-
sorted_channels.sort_by_key(|(_, channel)| {
-
.filter(|t| t.slug == channel.slug)
-
.map(|t| &t.created_at)
-
view::SortField::CreatedAt => {}
-
if matches!(view.sort.direction, view::SortDirection::Desc) {
-
sorted_channels.reverse();
-
let list = sorted_channels
-
.fold(column![].spacing(0), |col, (idx, channel)| {
-
let is_selected = state.selected_channel == Some(*idx);
-
let channel_info = format!("{} ({})", channel.name, channel.track_count);
-
let item = container(text(channel_info).size(14))
-
.style(move |_theme: &Theme| {
-
background: Some(iced::Background::Color(palette.gray3)),
-
container::Style::default()
-
let button = iced::widget::button(item)
-
.on_press(Message::ChannelSelected(*idx))
-
.style(|theme: &Theme, _status| iced::widget::button::Style {
-
text_color: theme.palette().text,
-
border: iced::Border::default(),
-
shadow: iced::Shadow::default(),
-
let col = col.push(button);
-
horizontal_rule(1).style(move |_theme: &Theme| iced::widget::rule::Style {
-
fill_mode: iced::widget::rule::FillMode::Full,
-
let content = scrollable(list);
-
.style(move |_theme: &Theme| container::Style {
-
background: Some(iced::Background::Color(palette.gray4)),
-
fn filtered_tracks<'a>(state: &'a State, view: &view::View) -> Vec<(usize, &'a Track)> {
-
.filter(|(_idx, track)| {
-
if let Some(ref slug) = view.filter.slug {
-
if &track.slug != slug {
-
if !view.filter.search_query.is_empty()
-
&& !matches_query(&track.title, &view.filter.search_query)
-
&& !matches_query(&track.description, &view.filter.search_query)
-
&& !matches_query(&track.slug, &view.filter.search_query)
-
fn render_tracks_view<'a>(
-
) -> iced::Element<'a, Message> {
-
let mut sorted_tracks = filtered_tracks(state, view);
-
match view.sort.field {
-
view::SortField::Name => {
-
sorted_tracks.sort_by(|(_, a), (_, b)| a.title.cmp(&b.title));
-
view::SortField::CreatedAt => {
-
sorted_tracks.sort_by(|(_, a), (_, b)| a.created_at.cmp(&b.created_at));
-
view::SortField::TrackCount | view::SortField::LatestTrack => {}
-
if matches!(view.sort.direction, view::SortDirection::Desc) {
-
sorted_tracks.reverse();
-
sorted_tracks.truncate(5000);
-
let list = sorted_tracks
-
.fold(column![].spacing(0), |col, (index, track)| {
-
let is_selected = state.selected_tracks.contains(index);
-
let slug_part = text(format!("@{}", track.slug))
-
let title_part = text(format!(" {}", track.title))
-
let track_content = if track.description.is_empty() {
-
row![slug_part, title_part].spacing(0)
-
let desc_part = text(format!(" {}", track.description))
-
.color(palette.text_dim);
-
row![slug_part, title_part, desc_part].spacing(0)
-
let item = container(track_content)
-
.style(move |_theme: &Theme| {
-
background: Some(iced::Background::Color(palette.gray3)),
-
container::Style::default()
-
let button = iced::widget::button(item)
-
.on_press(Message::TrackClicked(*index))
-
.style(|theme: &Theme, _status| iced::widget::button::Style {
-
text_color: theme.palette().text,
-
border: iced::Border::default(),
-
shadow: iced::Shadow::default(),
-
let col = col.push(button);
-
horizontal_rule(1).style(move |_theme: &Theme| iced::widget::rule::Style {
-
fill_mode: iced::widget::rule::FillMode::Full,
-
let content = scrollable(list);