refactor: power-confirm via PowerAction table (v0.6.14)
Align the power-confirm flow to moonset's ActionDef pattern, in lockstep with moongreet: a PowerAction table + create_power_button factory replace the two hand-wired reboot/shutdown handlers and the loose-param show_power_confirm. Add an in-flight re-trigger guard (power_box.set_sensitive(false)) and clear a stale error_label when showing a new prompt.
This commit is contained in:
+99
-59
@@ -170,55 +170,17 @@ pub fn create_lockscreen_window(
|
||||
power_box.set_margin_end(16);
|
||||
power_box.set_margin_bottom(16);
|
||||
|
||||
let reboot_btn = gtk::Button::new();
|
||||
reboot_btn.set_icon_name("system-reboot-symbolic");
|
||||
reboot_btn.add_css_class("power-button");
|
||||
reboot_btn.set_tooltip_text(Some(strings.reboot_tooltip));
|
||||
reboot_btn.connect_clicked(clone!(
|
||||
#[weak]
|
||||
confirm_area,
|
||||
#[strong]
|
||||
confirm_box,
|
||||
#[weak]
|
||||
error_label,
|
||||
move |_| {
|
||||
show_power_confirm(
|
||||
strings.reboot_confirm,
|
||||
power::reboot,
|
||||
strings.reboot_failed,
|
||||
strings,
|
||||
&confirm_area,
|
||||
&confirm_box,
|
||||
&error_label,
|
||||
);
|
||||
}
|
||||
));
|
||||
power_box.append(&reboot_btn);
|
||||
|
||||
let shutdown_btn = gtk::Button::new();
|
||||
shutdown_btn.set_icon_name("system-shutdown-symbolic");
|
||||
shutdown_btn.add_css_class("power-button");
|
||||
shutdown_btn.set_tooltip_text(Some(strings.shutdown_tooltip));
|
||||
shutdown_btn.connect_clicked(clone!(
|
||||
#[weak]
|
||||
confirm_area,
|
||||
#[strong]
|
||||
confirm_box,
|
||||
#[weak]
|
||||
error_label,
|
||||
move |_| {
|
||||
show_power_confirm(
|
||||
strings.shutdown_confirm,
|
||||
power::shutdown,
|
||||
strings.shutdown_failed,
|
||||
strings,
|
||||
&confirm_area,
|
||||
&confirm_box,
|
||||
&error_label,
|
||||
);
|
||||
}
|
||||
));
|
||||
power_box.append(&shutdown_btn);
|
||||
for action in power_actions() {
|
||||
let button = create_power_button(
|
||||
action,
|
||||
strings,
|
||||
&power_box,
|
||||
&confirm_area,
|
||||
&confirm_box,
|
||||
&error_label,
|
||||
);
|
||||
power_box.append(&button);
|
||||
}
|
||||
|
||||
overlay.add_overlay(&power_box);
|
||||
|
||||
@@ -688,23 +650,84 @@ fn set_default_avatar(
|
||||
image.set_icon_name(Some("avatar-default-symbolic"));
|
||||
}
|
||||
|
||||
/// Definition for a single power-action button (reboot, shutdown).
|
||||
/// Couples icon, prompt, error text and action so a button cannot be wired
|
||||
/// with a mismatched prompt/action pair. Mirrors moonset's `ActionDef`.
|
||||
#[derive(Clone, Copy)]
|
||||
struct PowerAction {
|
||||
icon_name: &'static str,
|
||||
tooltip_attr: fn(&Strings) -> &'static str,
|
||||
confirm_attr: fn(&Strings) -> &'static str,
|
||||
error_attr: fn(&Strings) -> &'static str,
|
||||
action_fn: fn() -> Result<(), PowerError>,
|
||||
}
|
||||
|
||||
/// The power actions offered on the lockscreen.
|
||||
fn power_actions() -> [PowerAction; 2] {
|
||||
[
|
||||
PowerAction {
|
||||
icon_name: "system-reboot-symbolic",
|
||||
tooltip_attr: |s| s.reboot_tooltip,
|
||||
confirm_attr: |s| s.reboot_confirm,
|
||||
error_attr: |s| s.reboot_failed,
|
||||
action_fn: power::reboot,
|
||||
},
|
||||
PowerAction {
|
||||
icon_name: "system-shutdown-symbolic",
|
||||
tooltip_attr: |s| s.shutdown_tooltip,
|
||||
confirm_attr: |s| s.shutdown_confirm,
|
||||
error_attr: |s| s.shutdown_failed,
|
||||
action_fn: power::shutdown,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Build a power-action icon button wired to the confirmation flow.
|
||||
fn create_power_button(
|
||||
action: PowerAction,
|
||||
strings: &'static Strings,
|
||||
power_box: >k::Box,
|
||||
confirm_area: >k::Box,
|
||||
confirm_box: &Rc<RefCell<Option<gtk::Box>>>,
|
||||
error_label: >k::Label,
|
||||
) -> gtk::Button {
|
||||
let button = gtk::Button::new();
|
||||
button.set_icon_name(action.icon_name);
|
||||
button.add_css_class("power-button");
|
||||
button.set_tooltip_text(Some((action.tooltip_attr)(strings)));
|
||||
button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
power_box,
|
||||
#[weak]
|
||||
confirm_area,
|
||||
#[strong]
|
||||
confirm_box,
|
||||
#[weak]
|
||||
error_label,
|
||||
move |_| {
|
||||
show_power_confirm(action, strings, &power_box, &confirm_area, &confirm_box, &error_label);
|
||||
}
|
||||
));
|
||||
button
|
||||
}
|
||||
|
||||
/// Show inline power confirmation.
|
||||
fn show_power_confirm(
|
||||
message: &'static str,
|
||||
action_fn: fn() -> Result<(), PowerError>,
|
||||
error_message: &'static str,
|
||||
action: PowerAction,
|
||||
strings: &'static Strings,
|
||||
power_box: >k::Box,
|
||||
confirm_area: >k::Box,
|
||||
confirm_box: &Rc<RefCell<Option<gtk::Box>>>,
|
||||
error_label: >k::Label,
|
||||
) {
|
||||
dismiss_power_confirm(confirm_area, confirm_box);
|
||||
error_label.set_visible(false);
|
||||
|
||||
let new_box = gtk::Box::new(gtk::Orientation::Vertical, 8);
|
||||
new_box.set_halign(gtk::Align::Center);
|
||||
new_box.set_margin_top(16);
|
||||
|
||||
let confirm_label = gtk::Label::new(Some(message));
|
||||
let confirm_label = gtk::Label::new(Some((action.confirm_attr)(strings)));
|
||||
confirm_label.add_css_class("confirm-label");
|
||||
new_box.append(&confirm_label);
|
||||
|
||||
@@ -714,6 +737,8 @@ fn show_power_confirm(
|
||||
let yes_btn = gtk::Button::with_label(strings.confirm_yes);
|
||||
yes_btn.add_css_class("confirm-yes");
|
||||
yes_btn.connect_clicked(clone!(
|
||||
#[weak]
|
||||
power_box,
|
||||
#[weak]
|
||||
confirm_area,
|
||||
#[strong]
|
||||
@@ -721,8 +746,7 @@ fn show_power_confirm(
|
||||
#[weak]
|
||||
error_label,
|
||||
move |_| {
|
||||
dismiss_power_confirm(&confirm_area, &confirm_box);
|
||||
execute_power_action(action_fn, error_message, &error_label);
|
||||
execute_power_action(action, strings, &power_box, &confirm_area, &confirm_box, &error_label);
|
||||
}
|
||||
));
|
||||
button_row.append(&yes_btn);
|
||||
@@ -753,28 +777,44 @@ fn dismiss_power_confirm(confirm_area: >k::Box, confirm_box: &Rc<RefCell<Optio
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a power action in a background thread.
|
||||
/// Execute a power action in a background thread, guarding against re-trigger.
|
||||
fn execute_power_action(
|
||||
action_fn: fn() -> Result<(), PowerError>,
|
||||
error_message: &'static str,
|
||||
action: PowerAction,
|
||||
strings: &'static Strings,
|
||||
power_box: >k::Box,
|
||||
confirm_area: >k::Box,
|
||||
confirm_box: &Rc<RefCell<Option<gtk::Box>>>,
|
||||
error_label: >k::Label,
|
||||
) {
|
||||
dismiss_power_confirm(confirm_area, confirm_box);
|
||||
|
||||
let action_fn = action.action_fn;
|
||||
let error_message = (action.error_attr)(strings);
|
||||
|
||||
// Desensitize the power buttons so a double-click or keyboard repeat cannot
|
||||
// fire the same action twice while it is in flight.
|
||||
power_box.set_sensitive(false);
|
||||
|
||||
glib::spawn_future_local(clone!(
|
||||
#[weak]
|
||||
power_box,
|
||||
#[weak]
|
||||
error_label,
|
||||
async move {
|
||||
let result = gio::spawn_blocking(move || action_fn()).await;
|
||||
let result = gio::spawn_blocking(action_fn).await;
|
||||
match result {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(e)) => {
|
||||
log::error!("Power action failed: {e}");
|
||||
error_label.set_text(error_message);
|
||||
error_label.set_visible(true);
|
||||
power_box.set_sensitive(true);
|
||||
}
|
||||
Err(_) => {
|
||||
log::error!("Power action panicked");
|
||||
error_label.set_text(error_message);
|
||||
error_label.set_visible(true);
|
||||
power_box.set_sensitive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user