feat: added reseting back to the default options in settings tabs (#818)
* feat: added reseting back to the default options in settings tabs * chore: removed the reset button for whole page and changed it to a reset button for every setting row * chore: removed old reset options from search index * chore: remove unused use_game_active and GameState::any_active * fix: reset global memory to the Default option instead of a fixed size * feat: add reset buttons to the launcher tab's system settings * chore: fold with_reset into resettable_all and drop unneeded dispatch clones * chore: rustfmt the import lists touched by the merge * fix: treat a missing discrete GPU setting as on * fix: read the discrete GPU reset default with the same true fallback --------- Co-authored-by: LynithDev <61880709+LynithDev@users.noreply.github.com>
This commit is contained in:
parent
35dcd584d0
commit
ac66752b99
9 changed files with 298 additions and 135 deletions
|
|
@ -45,6 +45,8 @@ pub fn use_start_maximized() -> bool {
|
|||
consume_root_context::<StartMaximizedState>().0
|
||||
}
|
||||
|
||||
pub const BROWSER_COMPAT_DEFAULT: bool = true;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BrowserCompatState(pub State<bool>);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ pub use overlay_claims::{
|
|||
pub use view_state::{PersistedView, use_view_state};
|
||||
|
||||
pub use active_cluster::{
|
||||
ActiveClusterState, BrowserCompatState, BrowserStateStore, BrowserTypeState, BrowserUiState,
|
||||
LinkConfirmState, OnboardingSelectionState, SplashState, StartMaximizedState,
|
||||
use_active_cluster_id, use_browser_compat, use_browser_state_store, use_browser_type,
|
||||
use_link_confirm, use_onboarding_selection, use_provide_active_cluster,
|
||||
ActiveClusterState, BROWSER_COMPAT_DEFAULT, BrowserCompatState, BrowserStateStore,
|
||||
BrowserTypeState, BrowserUiState, LinkConfirmState, OnboardingSelectionState, SplashState,
|
||||
StartMaximizedState, use_active_cluster_id, use_browser_compat, use_browser_state_store,
|
||||
use_browser_type, use_link_confirm, use_onboarding_selection, use_provide_active_cluster,
|
||||
use_provide_browser_compat, use_provide_browser_state, use_provide_browser_type,
|
||||
use_provide_link_confirm, use_provide_onboarding_selection, use_provide_splash,
|
||||
use_provide_start_maximized, use_splash, use_start_maximized,
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ use oneclient_core::images::BACKGROUND_IMAGE_EDGE;
|
|||
use oneclient_db::models::ClusterId;
|
||||
|
||||
use crate::hooks::{
|
||||
ActiveClusterState, BrowserCompatState, BrowserStateStore, BrowserTypeState,
|
||||
use_active_cluster_id, use_clusters, use_game_snapshot, use_launcher,
|
||||
ActiveClusterState, BROWSER_COMPAT_DEFAULT, BrowserCompatState, BrowserStateStore,
|
||||
BrowserTypeState, use_active_cluster_id, use_clusters, use_game_snapshot, use_launcher,
|
||||
use_provide_active_cluster, use_provide_browser_compat, use_provide_browser_state,
|
||||
use_provide_browser_type, use_splash,
|
||||
};
|
||||
|
|
@ -39,7 +39,7 @@ impl Component for AppShell {
|
|||
let active_cluster = use_state(|| None::<ClusterId>);
|
||||
use_provide_active_cluster(ActiveClusterState(active_cluster));
|
||||
|
||||
let browser_compat = use_state(|| true);
|
||||
let browser_compat = use_state(|| BROWSER_COMPAT_DEFAULT);
|
||||
use_provide_browser_compat(BrowserCompatState(browser_compat));
|
||||
|
||||
let browser_state = use_state(HashMap::new);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use freya::prelude::*;
|
|||
use super::settings_page;
|
||||
use crate::components::{IconType, TextInput};
|
||||
use crate::hooks::{use_dispatch, use_settings_snapshot};
|
||||
use crate::view::app::settings::{section_header, settings_row};
|
||||
use crate::view::app::settings::{resettable, section_header, settings_row};
|
||||
|
||||
fn normalize(value: &str) -> Option<String> {
|
||||
let trimmed = value.trim();
|
||||
|
|
@ -49,12 +49,12 @@ impl Component for SettingsApis {
|
|||
first.set(false);
|
||||
return;
|
||||
}
|
||||
let mut next = settings.clone();
|
||||
next.modrinth_api_key = normalize(&modrinth);
|
||||
next.curseforge_api_key = normalize(&curseforge);
|
||||
next.custom_api_endpoint = normalize(&endpoint);
|
||||
next.custom_meta_url_base = normalize(&meta_url_base);
|
||||
dispatch.set_settings(next);
|
||||
dispatch.edit_settings(|settings| {
|
||||
settings.modrinth_api_key = normalize(&modrinth);
|
||||
settings.curseforge_api_key = normalize(&curseforge);
|
||||
settings.custom_api_endpoint = normalize(&endpoint);
|
||||
settings.custom_meta_url_base = normalize(&meta_url_base);
|
||||
});
|
||||
});
|
||||
|
||||
settings_page()
|
||||
|
|
@ -63,34 +63,50 @@ impl Component for SettingsApis {
|
|||
IconType::Key01,
|
||||
"Modrinth",
|
||||
"Personal access token used for Modrinth requests.",
|
||||
TextInput::new(modrinth_key)
|
||||
.placeholder("Optional")
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(modrinth_key)
|
||||
.placeholder("Optional")
|
||||
.width(Size::px(220.)),
|
||||
modrinth_key,
|
||||
String::new(),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Key01,
|
||||
"CurseForge",
|
||||
"API key used for CurseForge requests.",
|
||||
TextInput::new(curseforge_key)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(curseforge_key)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
curseforge_key,
|
||||
String::new(),
|
||||
),
|
||||
))
|
||||
.child(section_header("ADVANCED"))
|
||||
.child(settings_row(
|
||||
IconType::Globe01,
|
||||
"Custom API Endpoint",
|
||||
"Override the default OneClient backend endpoint.",
|
||||
TextInput::new(custom_api_endpoint)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(custom_api_endpoint)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
custom_api_endpoint,
|
||||
String::new(),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Globe01,
|
||||
"Custom Meta URL Base",
|
||||
"Override the default bundles and versions data host.",
|
||||
TextInput::new(custom_meta_url_base)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(custom_meta_url_base)
|
||||
.placeholder("Default")
|
||||
.width(Size::px(220.)),
|
||||
custom_meta_url_base,
|
||||
String::new(),
|
||||
),
|
||||
))
|
||||
.into_element()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_core::settings::LauncherSettings;
|
||||
|
||||
use super::settings_page;
|
||||
use crate::components::{Icon, IconType, toggle};
|
||||
use crate::hooks::{use_dispatch, use_settings_snapshot};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::view::app::settings::{settings_row, settings_row_disabled};
|
||||
use crate::view::app::settings::{resettable, settings_row, settings_row_disabled};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ThemePreview {
|
||||
|
|
@ -52,6 +53,7 @@ impl Component for SettingsAppearance {
|
|||
let selected_theme = use_state(|| 0usize);
|
||||
|
||||
let settings = use_settings_snapshot().settings;
|
||||
let defaults = LauncherSettings::default();
|
||||
let dispatch = use_dispatch();
|
||||
|
||||
let dynamic_bg = use_state({
|
||||
|
|
@ -72,10 +74,10 @@ impl Component for SettingsAppearance {
|
|||
first.set(false);
|
||||
return;
|
||||
}
|
||||
let mut next = settings.clone();
|
||||
next.dynamic_background_enabled = parallax;
|
||||
next.animations_enabled = animations;
|
||||
dispatch.set_settings(next);
|
||||
dispatch.edit_settings(|settings| {
|
||||
settings.dynamic_background_enabled = parallax;
|
||||
settings.animations_enabled = animations;
|
||||
});
|
||||
});
|
||||
|
||||
settings_page()
|
||||
|
|
@ -88,8 +90,11 @@ impl Component for SettingsAppearance {
|
|||
.padding(Gaps::new(8., 0., 0., 0.))
|
||||
.child(accent_color_row())
|
||||
.child(custom_theme_row())
|
||||
.child(dynamic_background_row(dynamic_bg))
|
||||
.child(animations_row(animations_on)),
|
||||
.child(dynamic_background_row(
|
||||
dynamic_bg,
|
||||
defaults.dynamic_background_enabled,
|
||||
))
|
||||
.child(animations_row(animations_on, defaults.animations_enabled)),
|
||||
)
|
||||
.child(
|
||||
rect().padding(Gaps::new(8., 0., 0., 0.)).child(
|
||||
|
|
@ -245,20 +250,20 @@ fn custom_theme_row() -> impl IntoElement {
|
|||
)
|
||||
}
|
||||
|
||||
fn dynamic_background_row(enabled: State<bool>) -> impl IntoElement {
|
||||
fn dynamic_background_row(enabled: State<bool>, default: bool) -> impl IntoElement {
|
||||
settings_row(
|
||||
IconType::Eye,
|
||||
"Parallax background",
|
||||
"Make the home screen background drift with your cursor. Turning it off keeps the background but disables the motion.",
|
||||
toggle(enabled),
|
||||
resettable(toggle(enabled), enabled, default),
|
||||
)
|
||||
}
|
||||
|
||||
fn animations_row(animations_on: State<bool>) -> impl IntoElement {
|
||||
fn animations_row(animations_on: State<bool>, default: bool) -> impl IntoElement {
|
||||
settings_row(
|
||||
IconType::Play,
|
||||
"Animations",
|
||||
"Disable all launcher animations and transitions.",
|
||||
toggle(animations_on),
|
||||
resettable(toggle(animations_on), animations_on, default),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
use freya::prelude::*;
|
||||
use freya::router::RouterContext;
|
||||
use oneclient_core::settings::LauncherSettings;
|
||||
|
||||
use super::settings_page;
|
||||
use crate::Route;
|
||||
use crate::components::{IconType, link_button, toggle};
|
||||
use crate::hooks::{use_browser_compat, use_dispatch, use_settings_snapshot};
|
||||
use crate::view::app::settings::{section_header, settings_row};
|
||||
use crate::hooks::{
|
||||
BROWSER_COMPAT_DEFAULT, use_browser_compat, use_dispatch, use_settings_snapshot,
|
||||
};
|
||||
use crate::view::app::settings::{resettable, section_header, settings_row};
|
||||
use crate::view::console::open_log_console;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
|
@ -14,6 +17,7 @@ pub struct SettingsDeveloper;
|
|||
impl Component for SettingsDeveloper {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let settings = use_settings_snapshot().settings;
|
||||
let defaults = LauncherSettings::default();
|
||||
let dispatch = use_dispatch();
|
||||
|
||||
let log_debug = use_state({
|
||||
|
|
@ -28,9 +32,7 @@ impl Component for SettingsDeveloper {
|
|||
first.set(false);
|
||||
return;
|
||||
}
|
||||
let mut next = settings.clone();
|
||||
next.log_debug = enabled;
|
||||
dispatch.set_settings(next);
|
||||
dispatch.edit_settings(|settings| settings.log_debug = enabled);
|
||||
});
|
||||
|
||||
let browser_compat = use_browser_compat();
|
||||
|
|
@ -41,14 +43,18 @@ impl Component for SettingsDeveloper {
|
|||
IconType::SearchMd,
|
||||
"Compatible content only",
|
||||
"Filter the content browser to the active cluster's version and loader.",
|
||||
toggle(browser_compat),
|
||||
resettable(
|
||||
toggle(browser_compat),
|
||||
browser_compat,
|
||||
BROWSER_COMPAT_DEFAULT,
|
||||
),
|
||||
))
|
||||
.child(section_header("DEV TOOLS"))
|
||||
.child(settings_row(
|
||||
IconType::Sliders04,
|
||||
"Log Debug Info",
|
||||
"WARNING! This requires a restart to apply. Logs out debug info.",
|
||||
toggle(log_debug),
|
||||
resettable(toggle(log_debug), log_debug, defaults.log_debug),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Terminal,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use std::path::{Path, PathBuf};
|
|||
use freya::prelude::*;
|
||||
use freya::router::RouterContext;
|
||||
use oneclient_core::relocate::RelocationPlan;
|
||||
use oneclient_core::settings::{LaunchBehaviour, data_dir};
|
||||
use oneclient_core::settings::{LaunchBehaviour, LauncherSettings, data_dir};
|
||||
use oneclient_core::storage::format_bytes;
|
||||
|
||||
use super::{section_header, settings_page, settings_row};
|
||||
use super::{resettable, section_header, settings_page, settings_row};
|
||||
use crate::Route;
|
||||
use crate::components::{
|
||||
Button, Dropdown, Icon, IconType, OverlayPopup, open_folder_button, toggle,
|
||||
|
|
@ -24,6 +24,7 @@ pub struct SettingsLauncher;
|
|||
impl Component for SettingsLauncher {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let settings = use_settings_snapshot().settings;
|
||||
let defaults = LauncherSettings::default();
|
||||
let dispatch = use_dispatch();
|
||||
|
||||
let discord_rpc = use_state({
|
||||
|
|
@ -51,28 +52,32 @@ impl Component for SettingsLauncher {
|
|||
move || v
|
||||
});
|
||||
|
||||
let launch_behaviour = use_state({
|
||||
let v = settings.launch_behaviour;
|
||||
move || v
|
||||
});
|
||||
|
||||
let mut first = use_state(|| true);
|
||||
{
|
||||
let dispatch = dispatch.clone();
|
||||
use_side_effect(move || {
|
||||
let discord = *discord_rpc.read();
|
||||
let crash = *crash_reporting.read();
|
||||
let maximized = *start_maximized.read();
|
||||
let background = *run_in_background.read();
|
||||
let tray = *show_tray_icon.read();
|
||||
if *first.peek() {
|
||||
first.set(false);
|
||||
return;
|
||||
}
|
||||
dispatch.edit_settings(|next| {
|
||||
next.discord_enabled = discord;
|
||||
next.crash_reporting = crash;
|
||||
next.start_maximized = maximized;
|
||||
next.run_in_background = background;
|
||||
next.show_tray_icon = tray;
|
||||
});
|
||||
use_side_effect(move || {
|
||||
let discord = *discord_rpc.read();
|
||||
let crash = *crash_reporting.read();
|
||||
let maximized = *start_maximized.read();
|
||||
let background = *run_in_background.read();
|
||||
let tray = *show_tray_icon.read();
|
||||
let behaviour = *launch_behaviour.read();
|
||||
if *first.peek() {
|
||||
first.set(false);
|
||||
return;
|
||||
}
|
||||
dispatch.edit_settings(|next| {
|
||||
next.discord_enabled = discord;
|
||||
next.crash_reporting = crash;
|
||||
next.start_maximized = maximized;
|
||||
next.run_in_background = background;
|
||||
next.show_tray_icon = tray;
|
||||
next.launch_behaviour = behaviour;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// The only way back for someone who declined during onboarding
|
||||
let consent_summary = if settings.declined_tos {
|
||||
|
|
@ -94,19 +99,27 @@ impl Component for SettingsLauncher {
|
|||
IconType::Link03,
|
||||
"Discord RPC",
|
||||
"Enable Discord Rich Presence.",
|
||||
toggle(discord_rpc),
|
||||
resettable(toggle(discord_rpc), discord_rpc, defaults.discord_enabled),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::AlertTriangle,
|
||||
"Crash Reporting",
|
||||
"Send anonymous crash and error reports to help fix bugs. Applies on restart.",
|
||||
toggle(crash_reporting),
|
||||
resettable(
|
||||
toggle(crash_reporting),
|
||||
crash_reporting,
|
||||
defaults.crash_reporting,
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Maximize01,
|
||||
"Start Maximized",
|
||||
"Open the launcher window maximized. Applies on restart.",
|
||||
toggle(start_maximized),
|
||||
resettable(
|
||||
toggle(start_maximized),
|
||||
start_maximized,
|
||||
defaults.start_maximized,
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::File02,
|
||||
|
|
@ -119,19 +132,31 @@ impl Component for SettingsLauncher {
|
|||
IconType::Eye,
|
||||
"While Playing",
|
||||
"What the launcher window does once a game is running.",
|
||||
launch_behaviour_field(settings.launch_behaviour, dispatch),
|
||||
resettable(
|
||||
launch_behaviour_field(launch_behaviour),
|
||||
launch_behaviour,
|
||||
defaults.launch_behaviour,
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Moon01,
|
||||
"Run in Background",
|
||||
"Closing the window keeps OneClient running. Open it again from the tray icon or by launching it.",
|
||||
toggle(run_in_background),
|
||||
resettable(
|
||||
toggle(run_in_background),
|
||||
run_in_background,
|
||||
defaults.run_in_background,
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::LayoutTop,
|
||||
"Show Tray Icon",
|
||||
"Show OneClient in the system tray or menu bar. Applies on restart.",
|
||||
toggle(show_tray_icon),
|
||||
resettable(
|
||||
toggle(show_tray_icon),
|
||||
show_tray_icon,
|
||||
defaults.show_tray_icon,
|
||||
),
|
||||
))
|
||||
.child(section_header("FOLDERS AND FILES"))
|
||||
.child(DataFolder.into_element())
|
||||
|
|
@ -139,18 +164,18 @@ impl Component for SettingsLauncher {
|
|||
}
|
||||
}
|
||||
|
||||
fn launch_behaviour_field(selected: LaunchBehaviour, dispatch: Actions) -> impl IntoElement {
|
||||
fn launch_behaviour_field(mut selected: State<LaunchBehaviour>) -> impl IntoElement {
|
||||
let options: Vec<String> = LaunchBehaviour::ALL
|
||||
.iter()
|
||||
.map(|behaviour| behaviour.label().to_string())
|
||||
.collect();
|
||||
|
||||
Dropdown::new(selected.label(), options)
|
||||
Dropdown::new(selected.read().label(), options)
|
||||
.width(Size::px(220.))
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |idx: usize| {
|
||||
if let Some(behaviour) = LaunchBehaviour::ALL.get(idx).copied() {
|
||||
dispatch.edit_settings(|settings| settings.launch_behaviour = behaviour);
|
||||
selected.set(behaviour);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,15 @@ use freya::prelude::*;
|
|||
use oneclient_common::Patch;
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
use oneclient_core::settings::SettingsOsExtra;
|
||||
use oneclient_core::settings::{PackageUpdateMode, ProfileUpdate, Resolution};
|
||||
use oneclient_core::settings::{GameSettingsProfile, PackageUpdateMode, ProfileUpdate, Resolution};
|
||||
|
||||
use super::settings_page;
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
use crate::components::toggle_controlled;
|
||||
use crate::components::{
|
||||
Dropdown, Icon, IconType, TextInput, memory_field, toggle, validate_number,
|
||||
};
|
||||
use crate::hooks::{use_dispatch, use_settings_snapshot};
|
||||
use crate::theme::colors;
|
||||
use crate::view::app::settings::{section_header, settings_row};
|
||||
use crate::view::app::settings::{resettable, resettable_all, section_header, settings_row};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct SettingsMinecraft;
|
||||
|
|
@ -21,6 +19,7 @@ impl Component for SettingsMinecraft {
|
|||
fn render(&self) -> impl IntoElement {
|
||||
let settings = use_settings_snapshot().settings;
|
||||
let profile = settings.global_game_settings.clone();
|
||||
let defaults = GameSettingsProfile::default_global_profile();
|
||||
let dispatch = use_dispatch();
|
||||
|
||||
let fullscreen = use_state({
|
||||
|
|
@ -61,9 +60,18 @@ impl Component for SettingsMinecraft {
|
|||
let v = profile.hook_post.clone().unwrap_or_default();
|
||||
move || v
|
||||
});
|
||||
let update_mode = use_state({
|
||||
let v = profile.browser_update_mode.unwrap_or_default();
|
||||
move || v
|
||||
});
|
||||
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
let discrete_gpu = use_state({
|
||||
let v = profile.use_discrete_gpu();
|
||||
move || v
|
||||
});
|
||||
|
||||
let mut first = use_state(|| true);
|
||||
let batched = dispatch.clone();
|
||||
use_side_effect(move || {
|
||||
let update = build_update(
|
||||
*fullscreen.read(),
|
||||
|
|
@ -74,51 +82,92 @@ impl Component for SettingsMinecraft {
|
|||
&pre_launch_command.read(),
|
||||
&wrapper_command.read(),
|
||||
&post_exit_command.read(),
|
||||
*update_mode.read(),
|
||||
);
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
let gpu = *discrete_gpu.read();
|
||||
if *first.peek() {
|
||||
first.set(false);
|
||||
return;
|
||||
}
|
||||
batched.update_global_profile(update);
|
||||
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
let update = with_discrete_gpu(update, gpu);
|
||||
|
||||
dispatch.update_global_profile(update);
|
||||
});
|
||||
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
let discrete_gpu_default = SettingsOsExtra::default().use_discrete_gpu.unwrap_or(true);
|
||||
|
||||
let page = settings_page()
|
||||
.child(section_header("GAME"))
|
||||
.child(settings_row(
|
||||
IconType::Maximize01,
|
||||
"Force Fullscreen",
|
||||
"Force Minecraft to start in fullscreen mode.",
|
||||
toggle(fullscreen),
|
||||
resettable(
|
||||
toggle(fullscreen),
|
||||
fullscreen,
|
||||
defaults.force_fullscreen.unwrap_or(false),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::LayoutTop,
|
||||
"Resolution",
|
||||
"The game window resolution in pixels.",
|
||||
resolution_field(width, height),
|
||||
resettable_all(
|
||||
resolution_field(width, height),
|
||||
vec![
|
||||
(
|
||||
width,
|
||||
defaults
|
||||
.resolution
|
||||
.map(|r| r.width.to_string())
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
(
|
||||
height,
|
||||
defaults
|
||||
.resolution
|
||||
.map(|r| r.height.to_string())
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Database01,
|
||||
"Memory",
|
||||
"The amount of memory in megabytes allocated for the game. Presets leave 2 GB for the system.",
|
||||
memory_field(memory, "Default", oneclient_common::default_mem_max()),
|
||||
resettable(
|
||||
memory_field(memory, "Default", oneclient_common::default_mem_max()),
|
||||
memory,
|
||||
String::new(),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::Terminal,
|
||||
"JVM Arguments",
|
||||
"Extra arguments passed to Java. Separate them with spaces; quote values containing spaces.",
|
||||
TextInput::new(jvm_args)
|
||||
.placeholder("-XX:+UseG1GC")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(jvm_args)
|
||||
.placeholder("-XX:+UseG1GC")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
jvm_args,
|
||||
defaults.launch_args.clone().unwrap_or_default(),
|
||||
),
|
||||
))
|
||||
.child(section_header("CONTENT"))
|
||||
.child(settings_row(
|
||||
IconType::RefreshCw01,
|
||||
"Browser Package Updates",
|
||||
"What to do when content you installed from the browser has a newer version. Packs from bundles are not affected.",
|
||||
update_mode_field(
|
||||
profile.browser_update_mode.unwrap_or_default(),
|
||||
dispatch.clone(),
|
||||
resettable(
|
||||
update_mode_field(update_mode),
|
||||
update_mode,
|
||||
defaults.browser_update_mode.unwrap_or_default(),
|
||||
),
|
||||
))
|
||||
.child(section_header("PROCESS"))
|
||||
|
|
@ -126,64 +175,68 @@ impl Component for SettingsMinecraft {
|
|||
IconType::FilePlus02,
|
||||
"Pre-Launch Command",
|
||||
"Command to run before launching the game.",
|
||||
TextInput::new(pre_launch_command)
|
||||
.placeholder("echo 'Game started'")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(pre_launch_command)
|
||||
.placeholder("echo 'Game started'")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
pre_launch_command,
|
||||
defaults.hook_pre.clone().unwrap_or_default(),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::ParagraphWrap,
|
||||
"Wrapper Command",
|
||||
"Command to run when launching the game.",
|
||||
TextInput::new(wrapper_command)
|
||||
.placeholder("gamescope")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(wrapper_command)
|
||||
.placeholder("gamescope")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
wrapper_command,
|
||||
defaults.hook_wrapper.clone().unwrap_or_default(),
|
||||
),
|
||||
))
|
||||
.child(settings_row(
|
||||
IconType::FileX02,
|
||||
"Post-Exit Command",
|
||||
"Command to run after exiting the game.",
|
||||
TextInput::new(post_exit_command)
|
||||
.placeholder("echo 'Game exited'")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
resettable(
|
||||
TextInput::new(post_exit_command)
|
||||
.placeholder("echo 'Game exited'")
|
||||
.expandable(true)
|
||||
.width(Size::px(220.)),
|
||||
post_exit_command,
|
||||
defaults.hook_post.clone().unwrap_or_default(),
|
||||
),
|
||||
));
|
||||
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
let page = page
|
||||
.child(section_header("GRAPHICS"))
|
||||
.child(discrete_gpu_row(profile.os_extra.clone(), dispatch));
|
||||
.child(settings_row(
|
||||
IconType::Rocket02,
|
||||
"Use Discrete GPU",
|
||||
"Render the game on the dedicated graphics card. Does nothing on a machine with only one GPU, and draws noticeably more power on a laptop.",
|
||||
resettable(toggle(discrete_gpu), discrete_gpu, discrete_gpu_default),
|
||||
));
|
||||
|
||||
page.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
fn discrete_gpu_row(
|
||||
os_extra: Option<SettingsOsExtra>,
|
||||
dispatch: crate::Actions,
|
||||
) -> impl IntoElement {
|
||||
let current = os_extra.unwrap_or_default();
|
||||
let on = current.use_discrete_gpu.unwrap_or(true);
|
||||
fn with_discrete_gpu(mut update: ProfileUpdate, on: bool) -> ProfileUpdate {
|
||||
let base = crate::launcher::state()
|
||||
.ok()
|
||||
.and_then(|state| state.settings.read().global_game_settings.os_extra.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
let on_toggle: EventHandler<()> = (move |()| {
|
||||
dispatch.update_global_profile(ProfileUpdate {
|
||||
os_extra: Patch::Set(SettingsOsExtra {
|
||||
use_discrete_gpu: Some(!on),
|
||||
..current.clone()
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
})
|
||||
.into();
|
||||
|
||||
settings_row(
|
||||
IconType::Rocket02,
|
||||
"Use Discrete GPU",
|
||||
"Render the game on the dedicated graphics card. Does nothing on a machine with only one GPU, and draws noticeably more power on a laptop.",
|
||||
toggle_controlled(on, on_toggle),
|
||||
)
|
||||
update.os_extra = Patch::Set(SettingsOsExtra {
|
||||
use_discrete_gpu: Some(on),
|
||||
..base
|
||||
});
|
||||
update
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
@ -196,6 +249,7 @@ fn build_update(
|
|||
pre: &str,
|
||||
wrapper: &str,
|
||||
post: &str,
|
||||
update_mode: PackageUpdateMode,
|
||||
) -> ProfileUpdate {
|
||||
let resolution = match (width.trim(), height.trim()) {
|
||||
("", "") => Patch::Clear,
|
||||
|
|
@ -218,26 +272,23 @@ fn build_update(
|
|||
hook_pre: command_patch(pre),
|
||||
hook_wrapper: command_patch(wrapper),
|
||||
hook_post: command_patch(post),
|
||||
browser_update_mode: Patch::Set(update_mode),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatched separately from [`build_update`] which debounces keystrokes a dropdown has no intermediate states
|
||||
fn update_mode_field(selected: PackageUpdateMode, dispatch: crate::Actions) -> impl IntoElement {
|
||||
fn update_mode_field(mut selected: State<PackageUpdateMode>) -> impl IntoElement {
|
||||
let options: Vec<String> = PackageUpdateMode::ALL
|
||||
.iter()
|
||||
.map(|mode| mode.label().to_string())
|
||||
.collect();
|
||||
|
||||
Dropdown::new(selected.label(), options)
|
||||
Dropdown::new(selected.read().label(), options)
|
||||
.width(Size::px(220.))
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |idx: usize| {
|
||||
if let Some(mode) = PackageUpdateMode::ALL.get(idx).copied() {
|
||||
dispatch.update_global_profile(ProfileUpdate {
|
||||
browser_update_mode: Patch::Set(mode),
|
||||
..Default::default()
|
||||
});
|
||||
selected.set(mode);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub use minecraft::SettingsMinecraft;
|
|||
pub use storage::SettingsStorage;
|
||||
|
||||
use crate::{
|
||||
components::{Icon, IconType},
|
||||
components::{Button, Icon, IconType},
|
||||
theme::colors,
|
||||
};
|
||||
|
||||
|
|
@ -104,3 +104,61 @@ fn settings_row_inner(
|
|||
.child(trailing)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct ResetButton<T: Clone + PartialEq + 'static> {
|
||||
values: Vec<(State<T>, T)>,
|
||||
}
|
||||
|
||||
impl<T: Clone + PartialEq + 'static> Component for ResetButton<T> {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let at_default = self
|
||||
.values
|
||||
.iter()
|
||||
.all(|(value, default)| *value.read() == *default);
|
||||
|
||||
let values = self.values.clone();
|
||||
|
||||
Button::new()
|
||||
.ghost()
|
||||
.icon()
|
||||
.disabled(at_default)
|
||||
.alt(if at_default {
|
||||
"Already at the default"
|
||||
} else {
|
||||
"Reset to default"
|
||||
})
|
||||
.on_press(move |_| {
|
||||
for (value, default) in &values {
|
||||
let mut value = *value;
|
||||
value.set(default.clone());
|
||||
}
|
||||
})
|
||||
.child(
|
||||
Icon::new(IconType::RefreshCcw02)
|
||||
.size(14.)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resettable<T: Clone + PartialEq + 'static>(
|
||||
control: impl IntoElement,
|
||||
value: State<T>,
|
||||
default: T,
|
||||
) -> impl IntoElement {
|
||||
resettable_all(control, vec![(value, default)])
|
||||
}
|
||||
|
||||
pub fn resettable_all<T: Clone + PartialEq + 'static>(
|
||||
control: impl IntoElement,
|
||||
values: Vec<(State<T>, T)>,
|
||||
) -> impl IntoElement {
|
||||
rect()
|
||||
.horizontal()
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(8.)
|
||||
.child(control)
|
||||
.child(ResetButton { values })
|
||||
.into_element()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue