feat(ui): show all individual versions
This commit is contained in:
parent
76c4a03942
commit
57408aa710
6 changed files with 267 additions and 114 deletions
|
|
@ -24,13 +24,6 @@ impl ClusterLandscapeArt {
|
|||
selected,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_major(major: u32, selected: bool) -> Self {
|
||||
Self {
|
||||
art: DynamicArt::for_major(major).max_edge(512),
|
||||
selected,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ClusterLandscapeArt {
|
||||
|
|
|
|||
|
|
@ -28,15 +28,6 @@ impl DynamicArt {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn for_major(major: u32) -> Self {
|
||||
Self {
|
||||
major: Some(major),
|
||||
key: None,
|
||||
loader: None,
|
||||
max_edge: DEFAULT_IMAGE_EDGE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_cluster(cluster: &Cluster) -> Self {
|
||||
let parsed = parse_mc_version(&cluster.mc_version);
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ mod java_prompt;
|
|||
mod link_confirm;
|
||||
mod local_image;
|
||||
mod log_viewer;
|
||||
mod major_version_card;
|
||||
mod microsoft_login;
|
||||
mod navbar;
|
||||
mod notifications;
|
||||
|
|
@ -34,6 +33,7 @@ mod text_input;
|
|||
mod toasts;
|
||||
mod toggle;
|
||||
mod update_prompt;
|
||||
mod version_card;
|
||||
|
||||
pub use account_switcher::AccountSwitcher;
|
||||
pub use active_cluster_panel::ActiveClusterPanel;
|
||||
|
|
@ -52,7 +52,6 @@ pub use java_prompt::JavaPromptOverlay;
|
|||
pub use link_confirm::ConfirmLinkOverlay;
|
||||
pub use local_image::LocalImage;
|
||||
pub use log_viewer::LogViewer;
|
||||
pub use major_version_card::MajorVersionCard;
|
||||
pub use microsoft_login::use_microsoft_login;
|
||||
pub(crate) use microsoft_login::login_dialog;
|
||||
pub(crate) use navbar::window_controls;
|
||||
|
|
@ -74,3 +73,4 @@ pub use text_input::{TextInput, validate_number};
|
|||
pub use toasts::Toasts;
|
||||
pub use toggle::{toggle, toggle_controlled, toggle_disabled};
|
||||
pub use update_prompt::UpdatePromptOverlay;
|
||||
pub use version_card::VersionCard;
|
||||
|
|
|
|||
|
|
@ -1,48 +1,52 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_core::VersionKey;
|
||||
use oneclient_core::clusters::Cluster;
|
||||
|
||||
use crate::components::ClusterLandscapeArt;
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::utils::{loader_tags, major_pretty_name};
|
||||
use crate::utils::{ReleaseLine, line_art_key, line_title, loader_tags};
|
||||
|
||||
const CARD_HEIGHT_PX: f32 = 240.;
|
||||
|
||||
pub struct MajorVersionCard {
|
||||
pub major: u32,
|
||||
pub struct VersionCard {
|
||||
pub line: ReleaseLine,
|
||||
pub art_key: Option<VersionKey>,
|
||||
pub tags: Vec<String>,
|
||||
pub title: String,
|
||||
pub selected: bool,
|
||||
pub on_press: EventHandler<Event<PressEventData>>,
|
||||
}
|
||||
|
||||
impl MajorVersionCard {
|
||||
impl VersionCard {
|
||||
pub fn new(
|
||||
major: u32,
|
||||
line: ReleaseLine,
|
||||
clusters: &[Cluster],
|
||||
selected: bool,
|
||||
on_press: impl Into<EventHandler<Event<PressEventData>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
major,
|
||||
line,
|
||||
art_key: line_art_key(line, clusters),
|
||||
tags: loader_tags(clusters),
|
||||
title: major_pretty_name(major),
|
||||
title: line_title(line, clusters),
|
||||
selected,
|
||||
on_press: on_press.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MajorVersionCard {
|
||||
impl PartialEq for VersionCard {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.major == other.major
|
||||
self.line == other.line
|
||||
&& self.art_key == other.art_key
|
||||
&& self.tags == other.tags
|
||||
&& self.title == other.title
|
||||
&& self.selected == other.selected
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for MajorVersionCard {
|
||||
impl Component for VersionCard {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let mut hovering = use_state(|| false);
|
||||
|
||||
|
|
@ -69,7 +73,7 @@ impl Component for MajorVersionCard {
|
|||
};
|
||||
|
||||
rect()
|
||||
.key(self.major)
|
||||
.key(self.line)
|
||||
.width(Size::flex(1.0))
|
||||
.height(Size::px(CARD_HEIGHT_PX))
|
||||
.a11y_id(a11y_id)
|
||||
|
|
@ -94,7 +98,12 @@ impl Component for MajorVersionCard {
|
|||
.width(Size::fill())
|
||||
.height(Size::fill())
|
||||
.position(Position::new_absolute())
|
||||
.child(ClusterLandscapeArt::for_major(self.major, false)),
|
||||
.child(ClusterLandscapeArt::for_version(
|
||||
self.line.major,
|
||||
self.art_key,
|
||||
None,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
rect()
|
||||
|
|
@ -4,25 +4,91 @@ use std::collections::BTreeMap;
|
|||
use chrono::{Datelike, NaiveDate};
|
||||
use oneclient_core::clusters::Cluster;
|
||||
use oneclient_core::packages::domain::GameLoader;
|
||||
use oneclient_core::{VersionKey, format_mc_version, parse_mc_version};
|
||||
use oneclient_core::{ParsedMcVersion, VersionKey, format_mc_version, parse_mc_version};
|
||||
|
||||
pub type ClusterGroups = BTreeMap<u32, Vec<Cluster>>;
|
||||
pub type ClusterGroups = BTreeMap<ReleaseLine, Vec<Cluster>>;
|
||||
|
||||
/// One entry on the versions page. The modern scheme puts a full release in the
|
||||
/// first two components (`26.1`, `26.2`), so each minor is its own line; legacy
|
||||
/// `1.x` versions keep the whole major (`1.21`) as one line. A line that holds a
|
||||
/// single concrete version is presented as that version — see [`line_title`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ReleaseLine {
|
||||
pub major: u32,
|
||||
/// `Some` only for the modern scheme, where the minor is part of the line.
|
||||
pub minor: Option<u32>,
|
||||
}
|
||||
|
||||
impl ReleaseLine {
|
||||
fn from_parsed(parsed: &ParsedMcVersion) -> Option<Self> {
|
||||
let minor = if parsed.major >= 26 {
|
||||
Some(parsed.minor?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Self {
|
||||
major: parsed.major,
|
||||
minor,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_version(version: &str) -> Option<Self> {
|
||||
Self::from_parsed(&parse_mc_version(version)?)
|
||||
}
|
||||
|
||||
pub fn for_cluster(cluster: &Cluster) -> Option<Self> {
|
||||
Self::from_version(&cluster.mc_version)
|
||||
}
|
||||
|
||||
/// The generic name of the line: `26.1`, `1.21`.
|
||||
pub fn pretty_name(&self) -> String {
|
||||
match self.minor {
|
||||
Some(minor) => format!("{}.{minor}", self.major),
|
||||
None => format!("1.{}", self.major),
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata/art lookup key for the line itself, when no single version
|
||||
/// stands in for it.
|
||||
fn art_key(&self) -> Option<VersionKey> {
|
||||
self.minor.map(|minor| (minor, None))
|
||||
}
|
||||
}
|
||||
|
||||
fn cluster_key(cluster: &Cluster) -> Option<VersionKey> {
|
||||
parse_mc_version(&cluster.mc_version).and_then(|parsed| parsed.key())
|
||||
}
|
||||
|
||||
pub fn group_clusters_by_major(clusters: &[Cluster]) -> ClusterGroups {
|
||||
/// The one concrete version behind `clusters`, if they all share it.
|
||||
fn sole_version_key(clusters: &[Cluster]) -> Option<VersionKey> {
|
||||
match version_keys(clusters).as_slice() {
|
||||
[only] => Some(*only),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A line with a single version is shown as that full version (`26.1.2`);
|
||||
/// otherwise the generic line name (`1.21`), with the versions in a dropdown.
|
||||
pub fn line_title(line: ReleaseLine, clusters: &[Cluster]) -> String {
|
||||
match sole_version_key(clusters) {
|
||||
Some(key) => version_label(line.major, key),
|
||||
None => line.pretty_name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata/art key matching [`line_title`].
|
||||
pub fn line_art_key(line: ReleaseLine, clusters: &[Cluster]) -> Option<VersionKey> {
|
||||
sole_version_key(clusters).or_else(|| line.art_key())
|
||||
}
|
||||
|
||||
pub fn group_clusters_by_release(clusters: &[Cluster]) -> ClusterGroups {
|
||||
let mut groups: ClusterGroups = BTreeMap::new();
|
||||
|
||||
for cluster in clusters {
|
||||
let Some(parsed) = parse_mc_version(&cluster.mc_version) else {
|
||||
let Some(line) = ReleaseLine::for_cluster(cluster) else {
|
||||
continue;
|
||||
};
|
||||
groups
|
||||
.entry(parsed.major)
|
||||
.or_default()
|
||||
.push(cluster.clone());
|
||||
groups.entry(line).or_default().push(cluster.clone());
|
||||
}
|
||||
|
||||
for list in groups.values_mut() {
|
||||
|
|
@ -32,14 +98,6 @@ pub fn group_clusters_by_major(clusters: &[Cluster]) -> ClusterGroups {
|
|||
groups
|
||||
}
|
||||
|
||||
pub fn major_pretty_name(major: u32) -> String {
|
||||
if major >= 26 {
|
||||
format!("{major}")
|
||||
} else {
|
||||
format!("1.{major}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loader_tags(clusters: &[Cluster]) -> Vec<String> {
|
||||
let mut tags = Vec::new();
|
||||
for cluster in clusters {
|
||||
|
|
@ -67,7 +125,7 @@ pub fn version_keys(clusters: &[Cluster]) -> Vec<VersionKey> {
|
|||
keys
|
||||
}
|
||||
|
||||
pub fn loaders_for_major(clusters: &[Cluster]) -> Vec<GameLoader> {
|
||||
pub fn loaders_for_line(clusters: &[Cluster]) -> Vec<GameLoader> {
|
||||
let mut loaders = Vec::new();
|
||||
for cluster in clusters {
|
||||
if cluster.mc_loader.is_modded() && !loaders.contains(&cluster.mc_loader) {
|
||||
|
|
@ -93,13 +151,14 @@ pub fn resolve_cluster(
|
|||
.cloned()
|
||||
}
|
||||
|
||||
pub fn default_major(groups: &ClusterGroups, active: Option<Cluster>) -> Option<u32> {
|
||||
/// The line the page opens on: the active cluster's line, else the newest.
|
||||
pub fn default_line(groups: &ClusterGroups, active: Option<Cluster>) -> Option<ReleaseLine> {
|
||||
if let Some(cluster) = active
|
||||
&& let Some(parsed) = parse_mc_version(&cluster.mc_version)
|
||||
&& let Some(line) = ReleaseLine::for_cluster(&cluster)
|
||||
{
|
||||
return Some(parsed.major);
|
||||
return Some(line);
|
||||
}
|
||||
groups.keys().next().copied()
|
||||
groups.keys().next_back().copied()
|
||||
}
|
||||
|
||||
pub fn default_version_key(
|
||||
|
|
@ -119,7 +178,7 @@ pub fn default_version_key(
|
|||
}
|
||||
|
||||
pub fn default_loader(clusters: &[Cluster], preferred: Option<GameLoader>) -> Option<GameLoader> {
|
||||
let loaders = loaders_for_major(clusters);
|
||||
let loaders = loaders_for_line(clusters);
|
||||
if loaders.is_empty() {
|
||||
return clusters.first().map(|c| c.mc_loader);
|
||||
}
|
||||
|
|
@ -264,3 +323,92 @@ pub fn is_wayland() -> bool {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::view::onboarding::test_support::cluster;
|
||||
|
||||
fn versioned(id: i64, mc_version: &str) -> Cluster {
|
||||
Cluster {
|
||||
mc_version: mc_version.to_string(),
|
||||
..cluster(id)
|
||||
}
|
||||
}
|
||||
|
||||
fn line(major: u32, minor: Option<u32>) -> ReleaseLine {
|
||||
ReleaseLine { major, minor }
|
||||
}
|
||||
|
||||
fn lines_of(clusters: &[Cluster]) -> Vec<ReleaseLine> {
|
||||
group_clusters_by_release(clusters)
|
||||
.keys()
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modern_minors_are_separate_lines() {
|
||||
let clusters = [
|
||||
versioned(1, "26.1"),
|
||||
versioned(2, "26.2"),
|
||||
versioned(3, "26.2.1"),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
lines_of(&clusters),
|
||||
vec![line(26, Some(1)), line(26, Some(2))]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_minors_share_one_line() {
|
||||
let clusters = [versioned(1, "1.21.5"), versioned(2, "1.21.11")];
|
||||
|
||||
assert_eq!(lines_of(&clusters), vec![line(21, None)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_lone_version_is_shown_in_full() {
|
||||
let clusters = [versioned(1, "26.1.2")];
|
||||
let line = line(26, Some(1));
|
||||
|
||||
assert_eq!(line_title(line, &clusters), "26.1.2");
|
||||
assert_eq!(line_art_key(line, &clusters), Some((1, Some(2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn several_loaders_of_one_version_still_count_as_one() {
|
||||
let clusters = [
|
||||
versioned(1, "26.1.2"),
|
||||
Cluster {
|
||||
mc_loader: GameLoader::Vanilla,
|
||||
..versioned(2, "26.1.2")
|
||||
},
|
||||
];
|
||||
|
||||
assert_eq!(line_title(line(26, Some(1)), &clusters), "26.1.2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn several_versions_fall_back_to_the_generic_name() {
|
||||
let legacy = [
|
||||
versioned(1, "1.21.1"),
|
||||
versioned(2, "1.21.10"),
|
||||
versioned(3, "1.21.11"),
|
||||
];
|
||||
assert_eq!(line_title(line(21, None), &legacy), "1.21");
|
||||
assert_eq!(line_art_key(line(21, None), &legacy), None);
|
||||
|
||||
let modern = [versioned(4, "26.1"), versioned(5, "26.1.2")];
|
||||
assert_eq!(line_title(line(26, Some(1)), &modern), "26.1");
|
||||
assert_eq!(line_art_key(line(26, Some(1)), &modern), Some((1, None)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bare_legacy_version_keeps_its_line_name() {
|
||||
let clusters = [versioned(1, "1.21")];
|
||||
|
||||
assert_eq!(line_title(line(21, None), &clusters), "1.21");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use oneclient_core::VersionKey;
|
|||
use oneclient_core::clusters::Cluster;
|
||||
use oneclient_core::packages::domain::GameLoader;
|
||||
|
||||
use crate::components::{Button, ClusterLandscapeArt, Dropdown, Icon, IconType, MajorVersionCard};
|
||||
use crate::components::{
|
||||
Button, ClusterLandscapeArt, Dropdown, Icon, IconType, ScrollArea, VersionCard,
|
||||
};
|
||||
use crate::hooks::{
|
||||
use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot, use_launcher,
|
||||
use_version_metadata,
|
||||
|
|
@ -14,8 +16,8 @@ use crate::routes::Route;
|
|||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::utils::{
|
||||
default_loader, default_major, default_version_key, group_clusters_by_major, loaders_for_major,
|
||||
major_pretty_name, resolve_cluster, version_keys, version_label,
|
||||
ReleaseLine, default_line, default_loader, default_version_key, group_clusters_by_release,
|
||||
line_title, loaders_for_line, resolve_cluster, version_keys, version_label,
|
||||
};
|
||||
use crate::view::app::launch_button_state;
|
||||
|
||||
|
|
@ -32,7 +34,7 @@ impl Component for Clusters {
|
|||
fn render(&self) -> impl IntoElement {
|
||||
let clusters_query = use_clusters();
|
||||
let active_id = use_active_cluster_id();
|
||||
let mut selected_major = use_state(|| None::<u32>);
|
||||
let mut selected_line = use_state(|| None::<ReleaseLine>);
|
||||
let mut selected_version = use_state(|| None::<VersionKey>);
|
||||
let mut selected_loader = use_state(|| None::<GameLoader>);
|
||||
let mut grid_columns = use_state(|| 2_usize);
|
||||
|
|
@ -48,10 +50,11 @@ impl Component for Clusters {
|
|||
}
|
||||
};
|
||||
|
||||
let groups = group_clusters_by_major(&clusters);
|
||||
let majors: Vec<u32> = groups.keys().copied().collect();
|
||||
let groups = group_clusters_by_release(&clusters);
|
||||
// Newest version first.
|
||||
let lines: Vec<ReleaseLine> = groups.keys().rev().copied().collect();
|
||||
|
||||
if majors.is_empty() {
|
||||
if lines.is_empty() {
|
||||
return rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
|
|
@ -78,42 +81,42 @@ impl Component for Clusters {
|
|||
.read()
|
||||
.and_then(|id| clusters.iter().find(|c| c.id == id).cloned());
|
||||
|
||||
if selected_major.read().is_none() {
|
||||
*selected_major.write() = default_major(&groups, active_cluster.clone());
|
||||
if selected_line.read().is_none() {
|
||||
*selected_line.write() = default_line(&groups, active_cluster.clone());
|
||||
}
|
||||
|
||||
let major = (*selected_major.read())
|
||||
.filter(|m| groups.contains_key(m))
|
||||
.unwrap_or(majors[0]);
|
||||
let line = (*selected_line.read())
|
||||
.filter(|l| groups.contains_key(l))
|
||||
.unwrap_or(lines[0]);
|
||||
|
||||
if *selected_major.read() != Some(major) {
|
||||
*selected_major.write() = Some(major);
|
||||
if *selected_line.read() != Some(line) {
|
||||
*selected_line.write() = Some(line);
|
||||
}
|
||||
|
||||
let clusters_for_major = groups.get(&major).cloned().unwrap_or_default();
|
||||
let clusters_for_line = groups.get(&line).cloned().unwrap_or_default();
|
||||
|
||||
if selected_version.read().is_none() {
|
||||
let preferred = active_cluster
|
||||
.as_ref()
|
||||
.and_then(|c| oneclient_core::parse_mc_version(&c.mc_version))
|
||||
.and_then(|p| p.key());
|
||||
*selected_version.write() = default_version_key(&clusters_for_major, preferred);
|
||||
*selected_version.write() = default_version_key(&clusters_for_line, preferred);
|
||||
}
|
||||
|
||||
if selected_loader.read().is_none() {
|
||||
let preferred = active_cluster.as_ref().map(|c| c.mc_loader);
|
||||
*selected_loader.write() = default_loader(&clusters_for_major, preferred);
|
||||
*selected_loader.write() = default_loader(&clusters_for_line, preferred);
|
||||
}
|
||||
|
||||
let cluster = resolve_cluster(
|
||||
&clusters_for_major,
|
||||
&clusters_for_line,
|
||||
*selected_version.read(),
|
||||
*selected_loader.read(),
|
||||
)
|
||||
.or_else(|| clusters_for_major.first().cloned());
|
||||
.or_else(|| clusters_for_line.first().cloned());
|
||||
|
||||
let columns = *grid_columns.read();
|
||||
let grid_rows = chunk_majors(&majors, columns);
|
||||
let grid_rows = chunk_lines(&lines, columns);
|
||||
|
||||
rect()
|
||||
.vertical()
|
||||
|
|
@ -142,42 +145,49 @@ impl Component for Clusters {
|
|||
.vertical()
|
||||
.width(Size::flex(1.0))
|
||||
.height(Size::fill())
|
||||
.spacing(GRID_GAP_PX)
|
||||
.children(grid_rows.into_iter().map(|row| {
|
||||
let row_len = row.len();
|
||||
rect()
|
||||
.horizontal()
|
||||
.overflow(Overflow::Clip)
|
||||
.child(
|
||||
ScrollArea::new()
|
||||
.width(Size::fill())
|
||||
.content(Content::Flex)
|
||||
.height(Size::fill())
|
||||
.spacing(GRID_GAP_PX)
|
||||
.children(row.into_iter().map(|major| {
|
||||
let list = groups.get(&major).cloned().unwrap_or_default();
|
||||
let selected = *selected_major.read() == Some(major);
|
||||
let mut selected_major = selected_major;
|
||||
let mut selected_version = selected_version;
|
||||
let mut selected_loader = selected_loader;
|
||||
|
||||
MajorVersionCard::new(major, &list, selected, move |_| {
|
||||
*selected_major.write() = Some(major);
|
||||
*selected_version.write() = None;
|
||||
*selected_loader.write() = None;
|
||||
})
|
||||
.into_element()
|
||||
}))
|
||||
.children((row_len..columns).map(|_| {
|
||||
.children(grid_rows.into_iter().map(|row| {
|
||||
let row_len = row.len();
|
||||
rect()
|
||||
.width(Size::flex(1.0))
|
||||
.height(Size::px(CARD_HEIGHT_PX))
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.content(Content::Flex)
|
||||
.spacing(GRID_GAP_PX)
|
||||
.children(row.into_iter().map(|line| {
|
||||
let list =
|
||||
groups.get(&line).cloned().unwrap_or_default();
|
||||
let selected = *selected_line.read() == Some(line);
|
||||
let mut selected_line = selected_line;
|
||||
let mut selected_version = selected_version;
|
||||
let mut selected_loader = selected_loader;
|
||||
|
||||
VersionCard::new(line, &list, selected, move |_| {
|
||||
*selected_line.write() = Some(line);
|
||||
*selected_version.write() = None;
|
||||
*selected_loader.write() = None;
|
||||
})
|
||||
.into_element()
|
||||
}))
|
||||
.children((row_len..columns).map(|_| {
|
||||
rect()
|
||||
.width(Size::flex(1.0))
|
||||
.height(Size::px(CARD_HEIGHT_PX))
|
||||
.into_element()
|
||||
}))
|
||||
.into_element()
|
||||
}))
|
||||
.into_element()
|
||||
})),
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(match cluster {
|
||||
Some(cluster) => detail_sidebar(
|
||||
major,
|
||||
line,
|
||||
cluster,
|
||||
&clusters_for_major,
|
||||
&clusters_for_line,
|
||||
selected_version,
|
||||
selected_loader,
|
||||
),
|
||||
|
|
@ -188,9 +198,9 @@ impl Component for Clusters {
|
|||
}
|
||||
|
||||
fn detail_sidebar(
|
||||
major: u32,
|
||||
line: ReleaseLine,
|
||||
cluster: Cluster,
|
||||
clusters_for_major: &[Cluster],
|
||||
clusters_for_line: &[Cluster],
|
||||
selected_version: State<Option<VersionKey>>,
|
||||
selected_loader: State<Option<GameLoader>>,
|
||||
) -> Element {
|
||||
|
|
@ -199,14 +209,14 @@ fn detail_sidebar(
|
|||
let game = use_game_snapshot();
|
||||
let launcher = use_launcher();
|
||||
let syncing = launcher.fetching || launcher.syncing_bundles;
|
||||
let keys = version_keys(clusters_for_major);
|
||||
let loaders = loaders_for_major(clusters_for_major);
|
||||
let keys = version_keys(clusters_for_line);
|
||||
let loaders = loaders_for_line(clusters_for_line);
|
||||
let cluster_id = cluster.id;
|
||||
let version_title = major_pretty_name(major);
|
||||
let version_title = line_title(line, clusters_for_line);
|
||||
let version_value = *selected_version.read();
|
||||
let loader_value = *selected_loader.read();
|
||||
|
||||
let metadata = use_version_metadata(Some(major), version_value, loader_value);
|
||||
let metadata = use_version_metadata(Some(line.major), version_value, loader_value);
|
||||
|
||||
let heading = metadata
|
||||
.as_ref()
|
||||
|
|
@ -233,7 +243,7 @@ fn detail_sidebar(
|
|||
.border(border_all_color(1., colors::component_border()))
|
||||
.overflow(Overflow::Clip)
|
||||
.child(rect().width(Size::fill()).max_height(Size::px(140.)).child(
|
||||
ClusterLandscapeArt::for_version(major, version_value, loader_value, false),
|
||||
ClusterLandscapeArt::for_version(line.major, version_value, loader_value, false),
|
||||
))
|
||||
.child(
|
||||
rect()
|
||||
|
|
@ -263,7 +273,7 @@ fn detail_sidebar(
|
|||
.font_size(12.)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.children(version_rows(major, &keys, version_value, selected_version))
|
||||
.children(version_rows(line, &keys, version_value, selected_version))
|
||||
.children(loader_rows(&loaders, loader_value, selected_loader)),
|
||||
)
|
||||
.child(
|
||||
|
|
@ -312,8 +322,10 @@ fn tags_row(tags: &[String]) -> Option<Element> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Only shown when the line holds more than one version — a single-version line
|
||||
/// already names it on the card and in the heading.
|
||||
fn version_rows(
|
||||
major: u32,
|
||||
line: ReleaseLine,
|
||||
keys: &[VersionKey],
|
||||
selected: Option<VersionKey>,
|
||||
mut selected_version: State<Option<VersionKey>>,
|
||||
|
|
@ -322,14 +334,14 @@ fn version_rows(
|
|||
return None;
|
||||
}
|
||||
|
||||
let options: Vec<String> = keys.iter().map(|k| version_label(major, *k)).collect();
|
||||
let options: Vec<String> = keys.iter().map(|k| version_label(line.major, *k)).collect();
|
||||
let current = selected
|
||||
.and_then(|s| keys.iter().position(|k| *k == s))
|
||||
.unwrap_or(0);
|
||||
let keys = keys.to_vec();
|
||||
|
||||
Some(info_row(
|
||||
"Minor Version",
|
||||
"Version",
|
||||
Dropdown::new(options[current].clone(), options)
|
||||
.on_select(move |idx: usize| {
|
||||
if let Some(key) = keys.get(idx).copied() {
|
||||
|
|
@ -458,16 +470,16 @@ fn sidebar_error() -> Element {
|
|||
)
|
||||
.child(
|
||||
label()
|
||||
.text("Try selecting a different minor version or mod loader.")
|
||||
.text("Try selecting a different version or mod loader.")
|
||||
.font_size(14.)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
fn chunk_majors(majors: &[u32], columns: usize) -> Vec<Vec<u32>> {
|
||||
fn chunk_lines(lines: &[ReleaseLine], columns: usize) -> Vec<Vec<ReleaseLine>> {
|
||||
let columns = columns.max(1);
|
||||
majors.chunks(columns).map(|chunk| chunk.to_vec()).collect()
|
||||
lines.chunks(columns).map(|chunk| chunk.to_vec()).collect()
|
||||
}
|
||||
|
||||
fn grid_columns_for_width(available_width_px: f32) -> usize {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue