240 lines
8.2 KiB
Rust
240 lines
8.2 KiB
Rust
use std::{cell::RefCell, collections::HashMap, time::Duration};
|
|
|
|
use settings::{Setting, ToggleableSetting};
|
|
use galaxyui::{
|
|
elements::{
|
|
Container, CrossAxisAlignment, Flex, MainAxisAlignment, MouseStateHandle, ParentElement,
|
|
Text,
|
|
},
|
|
ui_components::{
|
|
components::{Coords, UiComponent, UiComponentStyles},
|
|
switch::SwitchStateHandle,
|
|
},
|
|
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle,
|
|
};
|
|
|
|
use crate::{
|
|
appearance::Appearance,
|
|
editor::{self, EditorView, SingleLineEditorOptions, TextOptions},
|
|
report_if_error,
|
|
settings_view::{
|
|
features_page::render_group,
|
|
settings_page::{render_body_item, LocalOnlyIconState, ToggleState},
|
|
},
|
|
undo_close::{settings::UndoCloseEnabled, UndoCloseSettings},
|
|
};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum Action {
|
|
ToggleUndoCloseEnabled,
|
|
UpdateGracePeriod,
|
|
}
|
|
|
|
/// A view containing settings relating to the undo close feature.
|
|
pub struct UndoCloseView {
|
|
/// State for the enable/disable toggle switch.
|
|
switch_state: SwitchStateHandle,
|
|
/// An editor for modifying the undo close grace period.
|
|
grace_period_editor: ViewHandle<EditorView>,
|
|
/// Whether or not the grace period value is valid.
|
|
is_grace_period_valid: bool,
|
|
/// State for the local only icon tooltip.
|
|
local_only_icon_states: RefCell<HashMap<String, MouseStateHandle>>,
|
|
}
|
|
|
|
impl UndoCloseView {
|
|
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
|
|
let grace_period_editor = ctx.add_typed_action_view(|ctx| {
|
|
EditorView::single_line(
|
|
SingleLineEditorOptions {
|
|
text: TextOptions::ui_font_size(Appearance::as_ref(ctx)),
|
|
..Default::default()
|
|
},
|
|
ctx,
|
|
)
|
|
});
|
|
|
|
ctx.subscribe_to_model(&UndoCloseSettings::handle(ctx), |me, _, _, ctx| {
|
|
// Update the value of the grace period input to match the new setting
|
|
me.grace_period_editor.update(ctx, |editor, ctx| {
|
|
editor.set_buffer_text(
|
|
&format!(
|
|
"{}",
|
|
UndoCloseSettings::handle(ctx)
|
|
.as_ref(ctx)
|
|
.grace_period
|
|
.as_secs_f32()
|
|
),
|
|
ctx,
|
|
);
|
|
});
|
|
ctx.notify()
|
|
});
|
|
|
|
ctx.subscribe_to_view(&grace_period_editor, move |me, _, event, ctx| {
|
|
me.handle_editor_event(event, ctx);
|
|
});
|
|
|
|
let grace_period = UndoCloseSettings::as_ref(ctx).grace_period.as_secs();
|
|
grace_period_editor.update(ctx, |editor, ctx| {
|
|
editor.set_buffer_text(&grace_period.to_string(), ctx);
|
|
});
|
|
Self {
|
|
switch_state: Default::default(),
|
|
local_only_icon_states: Default::default(),
|
|
grace_period_editor,
|
|
is_grace_period_valid: true,
|
|
}
|
|
}
|
|
|
|
/// This callback updates the undo close setting based on user input. If the
|
|
/// user hits Enter or the input loses focus, the new setting is saved (they
|
|
/// can also save it by clicking outside of the text field).
|
|
fn handle_editor_event(&mut self, event: &editor::Event, ctx: &mut ViewContext<Self>) {
|
|
use editor::Event;
|
|
match event {
|
|
Event::Edited(_) => {
|
|
let buffer_text = self.grace_period_editor.as_ref(ctx).buffer_text(ctx);
|
|
let new_validity = Self::parse_grace_period(&buffer_text).is_some();
|
|
if new_validity != self.is_grace_period_valid {
|
|
self.is_grace_period_valid = new_validity;
|
|
ctx.notify();
|
|
}
|
|
}
|
|
Event::Blurred | Event::Enter => {
|
|
self.handle_action(&Action::UpdateGracePeriod, ctx);
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
/// Parses user-entered text into a grace period duration, returning
|
|
/// None if the text isn't a valid grace period.
|
|
fn parse_grace_period(text: &str) -> Option<Duration> {
|
|
text.parse::<u64>().ok().map(Duration::from_secs)
|
|
}
|
|
|
|
/// Renders the editor for the grace period duration.
|
|
fn render_grace_period_editor(&self, appearance: &Appearance) -> Box<dyn Element> {
|
|
let theme = appearance.theme();
|
|
|
|
let border_color = if self.is_grace_period_valid {
|
|
None
|
|
} else {
|
|
Some(crate::themes::theme::Fill::error().into())
|
|
};
|
|
|
|
let editor_style = UiComponentStyles {
|
|
border_color,
|
|
width: Some(40.),
|
|
padding: Some(Coords::uniform(5.)),
|
|
background: Some(theme.surface_2().into()),
|
|
..Default::default()
|
|
};
|
|
Flex::row()
|
|
.with_cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
|
|
.with_child(
|
|
Container::new(
|
|
Text::new_inline(
|
|
"Grace period (seconds)",
|
|
appearance.ui_font_family(),
|
|
appearance.ui_font_size(),
|
|
)
|
|
.with_color(theme.active_ui_text_color().into())
|
|
.finish(),
|
|
)
|
|
.with_padding_right(8.5)
|
|
.finish(),
|
|
)
|
|
.with_child(
|
|
appearance
|
|
.ui_builder()
|
|
.text_input(self.grace_period_editor.clone())
|
|
.with_style(editor_style)
|
|
.build()
|
|
.finish(),
|
|
)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl Entity for UndoCloseView {
|
|
type Event = ();
|
|
}
|
|
|
|
impl View for UndoCloseView {
|
|
fn ui_name() -> &'static str {
|
|
"UndoCloseView"
|
|
}
|
|
|
|
fn render(&self, app: &AppContext) -> Box<dyn Element> {
|
|
let appearance = Appearance::as_ref(app);
|
|
let ui_builder = appearance.ui_builder();
|
|
|
|
let settings = UndoCloseSettings::as_ref(app);
|
|
let enabled = *settings.enabled;
|
|
|
|
let mut column = Flex::column()
|
|
.with_cross_axis_alignment(CrossAxisAlignment::Stretch)
|
|
.with_child(render_body_item::<Action>(
|
|
"Enable reopening of closed sessions".into(),
|
|
None,
|
|
LocalOnlyIconState::for_setting(
|
|
UndoCloseEnabled::storage_key(),
|
|
UndoCloseEnabled::sync_to_cloud(),
|
|
&mut self.local_only_icon_states.borrow_mut(),
|
|
app,
|
|
),
|
|
ToggleState::Enabled,
|
|
appearance,
|
|
ui_builder
|
|
.switch(self.switch_state.clone())
|
|
.check(enabled)
|
|
.build()
|
|
.on_click(move |ctx, _, _| {
|
|
ctx.dispatch_typed_action(Action::ToggleUndoCloseEnabled);
|
|
})
|
|
.finish(),
|
|
None,
|
|
));
|
|
|
|
if enabled {
|
|
column.add_child(render_group(
|
|
[self.render_grace_period_editor(appearance)],
|
|
appearance,
|
|
));
|
|
}
|
|
|
|
column.finish()
|
|
}
|
|
}
|
|
|
|
impl TypedActionView for UndoCloseView {
|
|
type Action = Action;
|
|
|
|
fn handle_action(&mut self, action: &Self::Action, ctx: &mut galaxyui::ViewContext<Self>) {
|
|
match action {
|
|
Action::ToggleUndoCloseEnabled => {
|
|
UndoCloseSettings::handle(ctx).update(ctx, |settings, ctx| {
|
|
report_if_error!(settings.enabled.toggle_and_save_value(ctx));
|
|
})
|
|
}
|
|
Action::UpdateGracePeriod => {
|
|
let grace_period_secs = self
|
|
.grace_period_editor
|
|
.read(ctx, |editor, ctx| editor.buffer_text(ctx));
|
|
let Some(grace_period) = Self::parse_grace_period(&grace_period_secs) else {
|
|
self.is_grace_period_valid = false;
|
|
return;
|
|
};
|
|
|
|
self.is_grace_period_valid = true;
|
|
UndoCloseSettings::handle(ctx).update(ctx, |settings, ctx| {
|
|
report_if_error!(settings.grace_period.set_value(grace_period, ctx));
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|