feat: list/grid layout toggle on clusters page
This commit is contained in:
parent
03c00b4199
commit
fc3046d60e
3 changed files with 103 additions and 36 deletions
|
|
@ -4,12 +4,13 @@ use oneclient_core::clusters::Cluster;
|
|||
use crate::components::{ART_PREVIEW_EDGE, DynamicArt};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::{border_all_color, last_played_label};
|
||||
use crate::utils::{GridSelection, ReleaseLine, line_art_key, line_title};
|
||||
|
||||
const ROW_HEIGHT_PX: f32 = 56.;
|
||||
const THUMB_PX: f32 = 40.;
|
||||
|
||||
pub struct InstanceRow {
|
||||
id: i64,
|
||||
key: GridSelection,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
played: String,
|
||||
|
|
@ -25,7 +26,7 @@ impl InstanceRow {
|
|||
on_press: impl Into<EventHandler<Event<PressEventData>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: cluster.id,
|
||||
key: GridSelection::Instance(cluster.id),
|
||||
title: cluster.name.clone(),
|
||||
subtitle: format!("{} \u{b7} {}", cluster.mc_version, cluster.mc_loader),
|
||||
played: last_played_label(cluster.last_played),
|
||||
|
|
@ -34,11 +35,30 @@ impl InstanceRow {
|
|||
on_press: on_press.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_line(
|
||||
line: ReleaseLine,
|
||||
clusters: &[Cluster],
|
||||
caption: String,
|
||||
selected: bool,
|
||||
on_press: impl Into<EventHandler<Event<PressEventData>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key: GridSelection::Line(line),
|
||||
title: line_title(line, clusters),
|
||||
subtitle: caption,
|
||||
played: last_played_label(clusters.iter().filter_map(|c| c.last_played).max()),
|
||||
art: DynamicArt::for_version(line.major, line_art_key(line, clusters), None)
|
||||
.max_edge(ART_PREVIEW_EDGE),
|
||||
selected,
|
||||
on_press: on_press.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InstanceRow {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
self.key == other.key
|
||||
&& self.title == other.title
|
||||
&& self.subtitle == other.subtitle
|
||||
&& self.played == other.played
|
||||
|
|
@ -76,7 +96,7 @@ impl Component for InstanceRow {
|
|||
};
|
||||
|
||||
rect()
|
||||
.key(self.id)
|
||||
.key(self.key)
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.height(Size::px(ROW_HEIGHT_PX))
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_common::VersionKey;
|
||||
use oneclient_core::clusters::Cluster;
|
||||
|
||||
use crate::components::{ART_PREVIEW_EDGE, DynamicArt};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::utils::{ReleaseLine, line_art_key, line_title};
|
||||
use crate::utils::{GridSelection, ReleaseLine, line_art_key, line_title};
|
||||
|
||||
const CARD_HEIGHT_PX: f32 = 150.;
|
||||
|
||||
pub struct VersionCard {
|
||||
pub line: ReleaseLine,
|
||||
pub art_key: Option<VersionKey>,
|
||||
pub key: GridSelection,
|
||||
pub art: DynamicArt,
|
||||
pub title: String,
|
||||
pub caption: String,
|
||||
pub count: usize,
|
||||
|
|
@ -28,8 +27,9 @@ impl VersionCard {
|
|||
on_press: impl Into<EventHandler<Event<PressEventData>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
line,
|
||||
art_key: line_art_key(line, clusters),
|
||||
key: GridSelection::Line(line),
|
||||
art: DynamicArt::for_version(line.major, line_art_key(line, clusters), None)
|
||||
.max_edge(ART_PREVIEW_EDGE),
|
||||
title: line_title(line, clusters),
|
||||
caption,
|
||||
count: clusters.len(),
|
||||
|
|
@ -37,12 +37,28 @@ impl VersionCard {
|
|||
on_press: on_press.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_instance(
|
||||
cluster: &Cluster,
|
||||
selected: bool,
|
||||
on_press: impl Into<EventHandler<Event<PressEventData>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key: GridSelection::Instance(cluster.id),
|
||||
art: DynamicArt::for_cluster(cluster).max_edge(ART_PREVIEW_EDGE),
|
||||
title: cluster.name.clone(),
|
||||
caption: format!("{} \u{b7} {}", cluster.mc_version, cluster.mc_loader),
|
||||
count: 1,
|
||||
selected,
|
||||
on_press: on_press.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for VersionCard {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.line == other.line
|
||||
&& self.art_key == other.art_key
|
||||
self.key == other.key
|
||||
&& self.art == other.art
|
||||
&& self.title == other.title
|
||||
&& self.caption == other.caption
|
||||
&& self.count == other.count
|
||||
|
|
@ -79,7 +95,7 @@ impl Component for VersionCard {
|
|||
};
|
||||
|
||||
rect()
|
||||
.key(self.line)
|
||||
.key(self.key)
|
||||
.width(Size::fill())
|
||||
.height(Size::px(CARD_HEIGHT_PX))
|
||||
.corner_radius(CornerRadius::new_all(16.))
|
||||
|
|
@ -97,10 +113,7 @@ impl Component for VersionCard {
|
|||
.height(Size::fill())
|
||||
.position(Position::new_absolute())
|
||||
.opacity(art_opacity)
|
||||
.child(
|
||||
DynamicArt::for_version(self.line.major, self.art_key, None)
|
||||
.max_edge(ART_PREVIEW_EDGE),
|
||||
),
|
||||
.child(self.art.clone()),
|
||||
)
|
||||
.child(
|
||||
rect()
|
||||
|
|
|
|||
|
|
@ -5,14 +5,16 @@ use freya::router::RouterContext;
|
|||
use oneclient_common::domain::GameLoader;
|
||||
use oneclient_common::{VersionKey, parse_mc_version};
|
||||
use oneclient_core::clusters::Cluster;
|
||||
use oneclient_core::settings::ViewLayout;
|
||||
|
||||
use crate::components::{
|
||||
ART_PREVIEW_EDGE, Button, Dropdown, DynamicArt, FilterMenu, FilterOption, Icon, IconType,
|
||||
InstanceRow, ScrollArea, TabBar, TabItem, TextInput, VersionCard, open_folder_button,
|
||||
InstanceRow, ScrollArea, Segment, SegmentedControl, TabBar, TabItem, TextInput, VersionCard,
|
||||
open_folder_button,
|
||||
};
|
||||
use crate::hooks::{
|
||||
settled_or_loading, use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot,
|
||||
use_launcher, use_version_metadata,
|
||||
use_launcher, use_version_metadata, use_view_state,
|
||||
};
|
||||
use crate::routes::Route;
|
||||
use crate::theme::colors;
|
||||
|
|
@ -97,6 +99,7 @@ impl Component for Clusters {
|
|||
let query = use_state(String::new);
|
||||
let loaders = use_state(Vec::<String>::new);
|
||||
let sort = use_state(|| Sort::RecentFirst);
|
||||
let layout = use_view_state("clusters").layout;
|
||||
let mut body_width =
|
||||
use_state(|| window_logical_size().width - PAGE_PADDING.left() - PAGE_PADDING.right());
|
||||
|
||||
|
|
@ -197,8 +200,20 @@ impl Component for Clusters {
|
|||
SIDEBAR_WIDTH_PX
|
||||
};
|
||||
let main_w = body_w - sidebar_w - COLUMN_GAP_PX;
|
||||
let card_columns = grid_columns_for_width(main_w, MAX_CARD_WIDTH_PX, CARD_GAP_PX);
|
||||
let row_columns = grid_columns_for_width(main_w, MAX_ROW_WIDTH_PX, ROW_GAP_PX);
|
||||
let grid = *layout.read() == ViewLayout::Grid;
|
||||
let (columns, item_height, gap) = if grid {
|
||||
(
|
||||
grid_columns_for_width(main_w, MAX_CARD_WIDTH_PX, CARD_GAP_PX),
|
||||
CARD_HEIGHT_PX,
|
||||
CARD_GAP_PX,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
grid_columns_for_width(main_w, MAX_ROW_WIDTH_PX, ROW_GAP_PX),
|
||||
ROW_HEIGHT_PX,
|
||||
ROW_GAP_PX,
|
||||
)
|
||||
};
|
||||
let inline_search = main_w >= INLINE_SEARCH_MIN_PX;
|
||||
|
||||
let active_filter = *filter.read();
|
||||
|
|
@ -229,7 +244,12 @@ impl Component for Clusters {
|
|||
clusters.iter().map(|c| c.mc_loader.to_string()).collect();
|
||||
available_loaders.sort();
|
||||
available_loaders.dedup();
|
||||
let filters = filter_menu(&available_loaders, loaders, sort);
|
||||
let filters = rect()
|
||||
.horizontal()
|
||||
.spacing(8.)
|
||||
.child(filter_menu(&available_loaders, loaders, sort))
|
||||
.child(layout_toggle(layout))
|
||||
.into_element();
|
||||
let nothing_shown =
|
||||
shown_lines.is_empty() && shown_custom.is_empty() && shown_packs.is_empty();
|
||||
|
||||
|
|
@ -255,11 +275,15 @@ impl Component for Clusters {
|
|||
(true, Some(c), _) | (_, _, [c]) => cluster_caption(c),
|
||||
_ => format!("{} instances", list.len()),
|
||||
};
|
||||
VersionCard::new(line, list, caption, is_selected, move |_| {
|
||||
let on_press = move |_| {
|
||||
selected.set(Some(item));
|
||||
selected_cluster.set(None);
|
||||
})
|
||||
.into_element()
|
||||
};
|
||||
if grid {
|
||||
VersionCard::new(line, list, caption, is_selected, on_press).into_element()
|
||||
} else {
|
||||
InstanceRow::for_line(line, list, caption, is_selected, on_press).into_element()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -267,8 +291,13 @@ impl Component for Clusters {
|
|||
list.iter()
|
||||
.map(|c| {
|
||||
let item = GridSelection::Instance(c.id);
|
||||
InstanceRow::new(c, current == Some(item), move |_| selected.set(Some(item)))
|
||||
.into_element()
|
||||
let is_selected = current == Some(item);
|
||||
let on_press = move |_| selected.set(Some(item));
|
||||
if grid {
|
||||
VersionCard::for_instance(c, is_selected, on_press).into_element()
|
||||
} else {
|
||||
InstanceRow::new(c, is_selected, on_press).into_element()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
|
@ -341,12 +370,7 @@ impl Component for Clusters {
|
|||
section(
|
||||
"OneClient",
|
||||
"Grouped by Minecraft version",
|
||||
fixed_grid(
|
||||
line_cards,
|
||||
card_columns,
|
||||
CARD_HEIGHT_PX,
|
||||
CARD_GAP_PX,
|
||||
),
|
||||
fixed_grid(line_cards, columns, item_height, gap),
|
||||
)
|
||||
}))
|
||||
.append_children(
|
||||
|
|
@ -367,9 +391,9 @@ impl Component for Clusters {
|
|||
caption,
|
||||
fixed_grid(
|
||||
instance_rows(list),
|
||||
row_columns,
|
||||
ROW_HEIGHT_PX,
|
||||
ROW_GAP_PX,
|
||||
columns,
|
||||
item_height,
|
||||
gap,
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
@ -543,6 +567,16 @@ fn filter_menu(
|
|||
.into_element()
|
||||
}
|
||||
|
||||
fn layout_toggle(layout: State<ViewLayout>) -> Element {
|
||||
SegmentedControl::new(layout)
|
||||
.height(34.)
|
||||
.icon_size(15.)
|
||||
.equal_width(34.)
|
||||
.segment(Segment::new(ViewLayout::List).icon(IconType::ParagraphWrap))
|
||||
.segment(Segment::new(ViewLayout::Grid).icon(IconType::DotsGrid))
|
||||
.into_element()
|
||||
}
|
||||
|
||||
fn search_input(query: State<String>, width: Size) -> Element {
|
||||
TextInput::new(query)
|
||||
.width(width)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue