improv(keyboard): shortcuts UI improvements

This commit is contained in:
Michael Aaron Murphy 2025-03-17 12:53:25 +01:00 committed by Michael Murphy
parent bcd8293c3e
commit 48e14d4add
20 changed files with 703 additions and 497 deletions

View file

@ -12,8 +12,8 @@ ashpd = { version = "0.9", default-features = false, features = [
"tokio",
], optional = true }
async-channel = "2.3.1"
chrono = "0.4.39"
clap = { version = "4.5.29", features = ["derive"] }
chrono = "0.4.40"
clap = { version = "4.5.32", features = ["derive"] }
color-eyre = "0.6.3"
cosmic-bg-config.workspace = true
cosmic-comp-config = { workspace = true, optional = true }
@ -34,7 +34,7 @@ derive_setters = "0.1.6"
dirs = "5.0.1"
downcast-rs = "1.2.1"
eyre = "0.6.12"
freedesktop-desktop-entry = "0.7.8"
freedesktop-desktop-entry = "0.7.9"
futures = "0.3.31"
hostname-validator = "1.1.1"
hostname1-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
@ -46,24 +46,24 @@ image = { version = "0.25", default-features = false, features = [
"webp",
"hdr",
] }
indexmap = "2.7.1"
indexmap = "2.8.0"
itertools = "0.13.0"
itoa = "1.0.14"
itoa = "1.0.15"
libcosmic.workspace = true
locale1 = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
mime-apps = { package = "cosmic-mime-apps", git = "https://github.com/pop-os/cosmic-mime-apps", optional = true }
notify = "6.1.1"
once_cell = "1.20.3"
once_cell = "1.21.1"
regex = "1.11.1"
ron = "0.8"
rust-embed = "8.5.0"
ron = "0.9.0"
rust-embed = "8.6.0"
sctk = { workspace = true, optional = true }
secure-string = "0.3.0"
serde = { version = "1.0.217", features = ["derive"] }
serde = { version = "1.0.219", features = ["derive"] }
slab = "0.4.9"
slotmap = "1.0.7"
static_init = "1.0.3"
sunrise = "1.0.1"
sunrise = "1.2.1"
tachyonix = "0.3.1"
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
tokio = { workspace = true, features = ["fs", "io-util", "sync"] }
@ -78,10 +78,10 @@ zbus = { version = "4.4.0", default-features = false, features = [
"tokio",
], optional = true }
zbus_polkit = { version = "4.0.0", optional = true }
fontdb = "0.16.2"
fontdb = "=0.16.2"
fixed_decimal = "0.5.6"
mime = "0.3.17"
rustix = "0.38.44"
rustix = { version = "1.0.3", features = ["process"] }
gettext-rs = { version = "0.7.2", features = [
"gettext-system",
], optional = true }

View file

@ -754,10 +754,7 @@ impl cosmic::Application for SettingsApp {
self.context_title = Some(title.to_string());
}
Message::CloseContextDrawer => {
self.core.window.show_context = false;
self.active_context_page = None;
}
Message::CloseContextDrawer => return self.close_context_drawer(),
Message::Error(error) => {
tracing::error!(error, "error occurred");
@ -869,7 +866,9 @@ impl SettingsApp {
if current_page != page {
self.loaded_pages.remove(&current_page);
close_context_drawer_task = cosmic::task::message(Message::CloseContextDrawer);
close_context_drawer_task = self.close_context_drawer();
leave_task = self
.pages
.on_leave(current_page)
@ -920,6 +919,16 @@ impl SettingsApp {
}
}
fn close_context_drawer(&mut self) -> Task<Message> {
self.core.window.show_context = false;
self.active_context_page = None;
self.pages
.on_context_drawer_close(self.active_page)
.unwrap_or(iced::Task::none())
.map(Message::PageMessage)
.map(Into::into)
}
/// Adds a main page to the settings application.
fn insert_page<P: page::AutoBind<crate::pages::Message>>(
&mut self,

View file

@ -39,6 +39,7 @@ macro_rules! fl {
// Get the `Localizer` to be used for localizing this library.
#[must_use]
#[inline(always)]
pub fn localizer() -> Box<dyn Localizer> {
Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
}

View file

@ -3,7 +3,7 @@
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{self, button, icon, settings, text};
use cosmic::{theme, Apply, Element, Task};
use cosmic::{Apply, Element, Task, theme};
use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_config::shortcuts::{self, Action, Binding, Shortcuts};
use cosmic_settings_page as page;
@ -15,7 +15,7 @@ use std::str::FromStr;
#[derive(Clone, Debug)]
pub enum ShortcutMessage {
AddKeybinding,
AddAnotherKeybinding,
ApplyReplace,
CancelReplace,
DeleteBinding(usize),
@ -32,8 +32,18 @@ pub struct ShortcutBinding {
pub id: widget::Id,
pub binding: Binding,
pub input: String,
pub editing: bool,
pub is_default: bool,
pub is_saved: bool,
}
impl ShortcutBinding {
pub fn reset(&mut self) {
self.input = if self.is_saved {
self.binding.to_string()
} else {
String::new()
};
}
}
#[must_use]
@ -57,8 +67,8 @@ impl ShortcutModel {
id: widget::Id::unique(),
binding: binding.clone(),
input: String::new(),
editing: false,
is_default,
is_saved: true,
});
(slab, if is_default { modified } else { modified + 1 })
@ -94,10 +104,13 @@ impl ShortcutModel {
#[must_use]
pub struct Model {
pub entity: page::Entity,
pub add_keybindings_button_id: cosmic::widget::Id,
pub defaults: Shortcuts,
pub editing: Option<usize>,
pub replace_dialog: Option<(usize, Binding, Action, String)>,
pub shortcut_models: Slab<ShortcutModel>,
pub shortcut_context: Option<usize>,
pub shortcut_title: String,
pub config: cosmic_config::Config,
pub custom: bool,
pub actions: fn(&Shortcuts, &Shortcuts) -> Slab<ShortcutModel>,
@ -107,10 +120,13 @@ impl Default for Model {
fn default() -> Self {
Self {
entity: page::Entity::null(),
add_keybindings_button_id: widget::Id::unique(),
defaults: Shortcuts::default(),
editing: None,
replace_dialog: None,
shortcut_models: Slab::new(),
shortcut_context: None,
shortcut_title: String::new(),
config: shortcuts::context().unwrap(),
custom: false,
actions: |_, _| Slab::new(),
@ -153,9 +169,16 @@ impl Model {
}
pub(super) fn context_drawer(&self) -> Option<Element<'_, ShortcutMessage>> {
self.shortcut_context
.as_ref()
.map(|id| context_drawer(&self.shortcut_models, *id, self.custom))
self.shortcut_context.as_ref().map(|id| {
context_drawer(
&self.shortcut_title,
&self.shortcut_models,
self.editing,
self.add_keybindings_button_id.clone(),
*id,
self.custom,
)
})
}
pub(super) fn dialog(&self) -> Option<Element<'_, ShortcutMessage>> {
@ -207,6 +230,25 @@ impl Model {
}
self.shortcut_models = (self.actions)(&self.defaults, &shortcuts);
self.shortcut_context = None;
self.editing = None;
}
pub(super) fn on_context_drawer_close(&mut self) {
if let Some(short_id) = self.shortcut_context.take() {
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(remove_id) = model
.bindings
.iter()
.find(|(_, binding)| !binding.is_saved)
.map(|(id, _)| id)
{
model.bindings.remove(remove_id);
}
}
}
self.editing = None;
}
pub(super) fn on_clear(&mut self) {
@ -249,17 +291,18 @@ impl Model {
#[allow(clippy::too_many_lines)]
pub(super) fn update(&mut self, message: ShortcutMessage) -> Task<crate::app::Message> {
match message {
ShortcutMessage::AddKeybinding => {
ShortcutMessage::AddAnotherKeybinding => {
if let Some(short_id) = self.shortcut_context {
if let Some(model) = self.shortcut_models.get_mut(short_id) {
// If an empty entry exists, focus it instead of creating a new input.
for (_, shortcut) in &mut model.bindings {
for (binding_id, shortcut) in &mut model.bindings {
if shortcut.binding.is_set()
|| Binding::from_str(&shortcut.input).is_ok()
{
continue;
}
self.editing = Some(binding_id);
shortcut.input.clear();
return widget::text_input::focus(shortcut.id.clone());
@ -267,13 +310,13 @@ impl Model {
// Create a new input and focus it.
let id = widget::Id::unique();
model.bindings.insert(ShortcutBinding {
self.editing = Some(model.bindings.insert(ShortcutBinding {
id: id.clone(),
binding: Binding::default(),
input: String::new(),
editing: true,
is_default: false,
});
is_saved: false,
}));
return widget::text_input::focus(id);
}
@ -306,7 +349,10 @@ impl Model {
shortcut.binding = new_binding.clone();
shortcut.input.clear();
shortcut.editing = false;
if self.editing == Some(id) {
self.editing = None;
}
let action = model.action.clone();
self.config_remove(&prev_binding);
@ -319,7 +365,18 @@ impl Model {
}
}
ShortcutMessage::CancelReplace => self.replace_dialog = None,
ShortcutMessage::CancelReplace => {
if let Some(((id, _, _, _), short_id)) =
self.replace_dialog.take().zip(self.shortcut_context)
{
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(binding) = model.bindings.get_mut(id) {
binding.reset();
return cosmic::widget::text_input::focus(binding.id.clone());
}
}
}
}
ShortcutMessage::DeleteBinding(id) => {
if let Some(short_id) = self.shortcut_context {
@ -328,14 +385,8 @@ impl Model {
if shortcut.is_default {
self.config_add(Action::Disable, shortcut.binding.clone());
} else {
// if last keybind deleted, clear shortcut context
if model.bindings.is_empty() {
self.shortcut_context = None;
}
self.config_remove(&shortcut.binding);
}
self.on_enter();
}
}
}
@ -344,7 +395,6 @@ impl Model {
let model = self.shortcut_models.remove(id);
for (_, shortcut) in model.bindings {
self.config_remove(&shortcut.binding);
self.on_enter();
}
}
@ -352,10 +402,12 @@ impl Model {
if let Some(short_id) = self.shortcut_context {
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(shortcut) = model.bindings.get_mut(id) {
shortcut.editing = enable;
if enable {
self.editing = Some(id);
shortcut.input = shortcut.binding.to_string();
return widget::text_input::select_all(shortcut.id.clone());
} else if self.editing == Some(id) {
self.editing = None;
}
}
}
@ -395,14 +447,16 @@ impl Model {
ShortcutMessage::ShowShortcut(id, description) => {
self.shortcut_context = Some(id);
self.shortcut_title = description;
self.replace_dialog = None;
let mut tasks = vec![cosmic::task::message(
crate::app::Message::OpenContextDrawer(self.entity, description.into()),
crate::app::Message::OpenContextDrawer(self.entity, "".into()),
)];
if let Some(model) = self.shortcut_models.get(0) {
if let Some(shortcut) = model.bindings.get(0) {
self.editing = Some(0);
tasks.push(widget::text_input::focus(shortcut.id.clone()));
tasks.push(widget::text_input::select_all(shortcut.id.clone()));
}
@ -411,59 +465,7 @@ impl Model {
return Task::batch(tasks);
}
ShortcutMessage::SubmitBinding(id) => {
if let Some(short_id) = self.shortcut_context {
let mut apply_binding = None;
// Check for conflicts with the new binding.
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(shortcut) = model.bindings.get_mut(id) {
match Binding::from_str(&shortcut.input) {
Ok(new_binding) => {
if !new_binding.is_set() {
shortcut.input.clear();
return Task::none();
}
if let Some(action) = self.config_contains(&new_binding) {
let action_str = if let Action::Spawn(_) = &action {
super::localize_custom_action(&action, &new_binding)
} else {
super::localize_action(&action)
};
self.replace_dialog =
Some((id, new_binding, action, action_str));
return Task::none();
}
apply_binding = Some(new_binding);
}
Err(why) => {
tracing::error!(why, "keybinding input invalid");
}
}
}
}
// Apply if no conflict was found.
if let Some(new_binding) = apply_binding {
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(shortcut) = model.bindings.get_mut(id) {
let prev_binding = shortcut.binding.clone();
shortcut.binding = new_binding.clone();
shortcut.input.clear();
shortcut.editing = false;
let action = model.action.clone();
self.config_remove(&prev_binding);
self.config_add(action, new_binding);
self.on_enter();
}
}
}
}
}
ShortcutMessage::SubmitBinding(id) => return self.submit_binding(id),
}
Task::none()
@ -476,13 +478,87 @@ impl Model {
.fold(widget::list_column(), widget::ListColumn::add)
.into()
}
fn submit_binding(&mut self, id: usize) -> Task<crate::app::Message> {
if let Some(short_id) = self.shortcut_context {
let mut apply_binding = None;
// Check for conflicts with the new binding.
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(shortcut) = model.bindings.get_mut(id) {
if shortcut.input.is_empty() {
return Task::none();
}
match Binding::from_str(&shortcut.input) {
Ok(new_binding) => {
if shortcut.binding == new_binding {
return Task::none();
}
if !new_binding.is_set() {
shortcut.input.clear();
return Task::none();
}
if let Some(action) = self.config_contains(&new_binding) {
let action_str = if let Action::Spawn(_) = &action {
super::localize_custom_action(&action, &new_binding)
} else {
super::localize_action(&action)
};
self.replace_dialog = Some((id, new_binding, action, action_str));
return Task::none();
}
apply_binding = Some(new_binding);
}
Err(why) => {
tracing::error!(why, "keybinding input invalid");
shortcut.reset();
}
}
}
}
// Apply if no conflict was found.
if let Some(new_binding) = apply_binding {
if let Some(model) = self.shortcut_models.get_mut(short_id) {
if let Some(shortcut) = model.bindings.get_mut(id) {
let prev_binding = shortcut.binding.clone();
shortcut.binding = new_binding.clone();
shortcut.is_saved = true;
shortcut.input.clear();
if self.editing == Some(id) {
self.editing = None;
}
let action = model.action.clone();
self.config_remove(&prev_binding);
self.config_add(action, new_binding);
return cosmic::widget::text_input::focus(
self.add_keybindings_button_id.clone(),
);
}
}
}
}
Task::none()
}
}
fn context_drawer(
shortcuts: &Slab<ShortcutModel>,
fn context_drawer<'a>(
title: &'a str,
shortcuts: &'a Slab<ShortcutModel>,
editing: Option<usize>,
add_keybindings_id: widget::Id,
id: usize,
show_action: bool,
) -> Element<ShortcutMessage> {
) -> Element<'a, ShortcutMessage> {
let cosmic::cosmic_theme::Spacing {
space_xxs,
space_xs,
@ -505,35 +581,39 @@ fn context_drawer(
let bindings = model.bindings.iter().enumerate().fold(
widget::list_column().spacing(space_xxs),
|section, (_, (bind_id, shortcut))| {
let text: Cow<'_, str> = if !shortcut.editing && shortcut.binding.is_set() {
let editing = editing == Some(bind_id);
let text: Cow<'_, str> = if !editing && shortcut.binding.is_set() {
Cow::Owned(shortcut.binding.to_string())
} else {
Cow::Borrowed(&shortcut.input)
};
let input = widget::editable_input("", text, shortcut.editing, move |enable| {
let input = widget::editable_input("", text, editing, move |enable| {
ShortcutMessage::EditBinding(bind_id, enable)
})
.select_on_focus(true)
.on_input(move |text| ShortcutMessage::InputBinding(bind_id, text))
.on_unfocus(ShortcutMessage::SubmitBinding(bind_id))
.on_submit(move |_| ShortcutMessage::SubmitBinding(bind_id))
.padding([0, space_xs])
.id(shortcut.id.clone())
.into();
let delete_button = widget::button::icon(icon::from_name("edit-delete-symbolic"))
.on_press(ShortcutMessage::DeleteBinding(bind_id))
.into();
let mut children = Vec::with_capacity(2);
children.push(input);
let flex_control =
settings::item_row(vec![input, delete_button]).align_y(Alignment::Center);
if shortcut.is_saved {
let delete_button = widget::button::icon(icon::from_name("edit-delete-symbolic"))
.on_press(ShortcutMessage::DeleteBinding(bind_id))
.into();
children.push(delete_button);
}
section.add(flex_control)
section.add(settings::item_row(children).align_y(Alignment::Center))
},
);
// TODO: Detect when it is necessary
let reset_keybinding_button = if show_action {
let reset_keybinding_button = if model.modified == 0 || show_action {
None
} else {
let button = widget::button::standard(fl!("reset-to-default"))
@ -541,8 +621,13 @@ fn context_drawer(
Some(button)
};
let add_keybinding_button =
widget::button::standard(fl!("add-keybinding")).on_press(ShortcutMessage::AddKeybinding);
let add_keybinding_button = widget::button::standard(fl!("add-another-keybinding"))
.id(add_keybindings_id)
.on_press_maybe(if model.bindings.iter().any(|(_, b)| !b.is_saved) {
None
} else {
Some(ShortcutMessage::AddAnotherKeybinding)
});
let button_container = widget::row::with_capacity(2)
.push_maybe(reset_keybinding_button)
@ -552,7 +637,8 @@ fn context_drawer(
.width(Length::Fill)
.align_x(Alignment::End);
widget::column::with_capacity(if show_action { 3 } else { 2 })
widget::column::with_capacity(if show_action { 4 } else { 3 })
.push(widget::text::heading(title))
.spacing(space_l)
.push_maybe(action)
.push(bindings)

View file

@ -67,9 +67,10 @@ pub enum Message {
#[derive(Default)]
struct AddShortcut {
pub active: bool,
pub editing: Option<usize>,
pub name: String,
pub task: String,
pub keys: Slab<(String, widget::Id, bool)>,
pub keys: Slab<(String, widget::Id)>,
}
impl AddShortcut {
@ -79,8 +80,7 @@ impl AddShortcut {
self.task.clear();
if self.keys.is_empty() {
self.keys
.insert((String::new(), widget::Id::unique(), false));
self.keys.insert((String::new(), widget::Id::unique()));
} else {
while self.keys.len() > 1 {
self.keys.remove(self.keys.len() - 1);
@ -103,34 +103,20 @@ impl Page {
}
Message::KeyEditing(id, enable) => {
self.add_shortcut.keys[id].2 = enable;
if enable {
self.add_shortcut.editing = Some(id)
} else if self.add_shortcut.editing == Some(id) {
let task = self.add_keybinding();
self.add_shortcut.editing = None;
return task;
}
}
Message::NameInput(text) => {
self.add_shortcut.name = text;
}
Message::AddKeybinding => {
// If an empty entry exists, focus it instead of creating a new input.
for (_, (binding, id, _)) in &mut self.add_shortcut.keys {
if Binding::from_str(binding).is_ok() {
continue;
}
binding.clear();
return widget::text_input::focus(id.clone());
}
let new_id = widget::Id::unique();
self.add_shortcut
.keys
.insert((String::new(), new_id.clone(), true));
return Task::batch(vec![
widget::text_input::focus(new_id.clone()),
widget::text_input::select_all(new_id),
]);
}
Message::AddKeybinding => return self.add_keybinding(),
Message::AddShortcut => {
let name = self.add_shortcut.name.trim();
@ -172,12 +158,13 @@ impl Page {
}
Message::EditCombination => {
let (_, id, editing) = &mut self.add_shortcut.keys[0];
*editing = true;
return Task::batch(vec![
widget::text_input::focus(id.clone()),
widget::text_input::select_all(id.clone()),
]);
if let Some((slab_index, (_, id))) = self.add_shortcut.keys.iter().next() {
self.add_shortcut.editing = Some(slab_index);
return Task::batch(vec![
widget::text_input::focus(id.clone()),
widget::text_input::select_all(id.clone()),
]);
}
}
Message::NameSubmit => {
@ -227,6 +214,31 @@ impl Page {
Task::none()
}
fn add_keybinding(&mut self) -> Task<crate::app::Message> {
// If an empty entry exists, focus it instead of creating a new input.
for (_, (binding, id)) in &mut self.add_shortcut.keys {
if Binding::from_str(binding).is_ok() {
continue;
}
binding.clear();
return widget::text_input::focus(id.clone());
}
let new_id = widget::Id::unique();
self.add_shortcut.editing = Some(
self.add_shortcut
.keys
.insert((String::new(), new_id.clone())),
);
Task::batch(vec![
widget::text_input::focus(new_id.clone()),
widget::text_input::select_all(new_id),
])
}
fn add_keybinding_context(&self) -> Element<'_, Message> {
let name_input = widget::text_input("", &self.add_shortcut.name)
.padding([6, 12])
@ -258,15 +270,17 @@ impl Page {
let keys = self.add_shortcut.keys.iter().fold(
widget::list_column().spacing(0),
|column, (id, (text, widget_id, editing))| {
|column, (id, (text, widget_id))| {
let key_combination = widget::editable_input(
fl!("type-key-combination"),
text,
*editing,
self.add_shortcut.editing == Some(id),
move |enable| Message::KeyEditing(id, enable),
)
.select_on_focus(true)
.padding([0, 12])
.on_input(move |input| Message::KeyInput(id, input))
.on_unfocus(Message::AddKeybinding)
.on_submit(|_| Message::AddKeybinding)
.id(widget_id.clone())
.apply(widget::container)
@ -278,7 +292,7 @@ impl Page {
let controls = widget::list_column().add(input_fields).add(keys).spacing(0);
let add_keybinding_button = widget::button::standard(fl!("add-keybinding"))
let add_keybinding_button = widget::button::standard(fl!("add-another-keybinding"))
.on_press(Message::AddShortcut)
.apply(widget::container)
.width(Length::Fill)
@ -358,6 +372,11 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::CustomShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
@ -385,8 +404,8 @@ fn bindings(_defaults: &Shortcuts, keybindings: &Shortcuts) -> Slab<ShortcutMode
id: widget::Id::unique(),
binding: binding.clone(),
input: String::new(),
editing: false,
is_default: false,
is_saved: true,
};
if let Some((_, existing_model)) =

View file

@ -59,9 +59,13 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::ManageWindowShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
}

View file

@ -14,13 +14,13 @@ pub mod tiling;
use cosmic::iced::Length;
use cosmic::widget::{self, icon, settings, text};
use cosmic::{theme, Apply, Element, Task};
use cosmic::{Apply, Element, Task, theme};
use cosmic_config::ConfigGet;
use cosmic_settings_config::Binding;
use cosmic_settings_config::shortcuts::action::{
Direction, FocusDirection, Orientation, ResizeDirection,
};
use cosmic_settings_config::shortcuts::{self, Action, Shortcuts};
use cosmic_settings_config::Binding;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use itertools::Itertools;
@ -476,25 +476,21 @@ fn all_system_actions() -> &'static [Action] {
Action::Focus(FocusDirection::Up),
Action::LastWorkspace,
Action::Maximize,
Action::MigrateWorkspaceToNextOutput,
Action::MigrateWorkspaceToOutput(Direction::Down),
Action::MigrateWorkspaceToOutput(Direction::Left),
Action::MigrateWorkspaceToOutput(Direction::Right),
Action::MigrateWorkspaceToOutput(Direction::Up),
Action::MigrateWorkspaceToPreviousOutput,
Action::Minimize,
Action::Move(Direction::Down),
Action::Move(Direction::Left),
Action::Move(Direction::Right),
Action::Move(Direction::Up),
Action::MoveToLastWorkspace,
Action::MoveToNextOutput,
Action::MoveToNextWorkspace,
Action::MoveToOutput(Direction::Down),
Action::MoveToOutput(Direction::Left),
Action::MoveToOutput(Direction::Right),
Action::MoveToOutput(Direction::Up),
Action::MoveToPreviousOutput,
Action::MoveToPreviousWorkspace,
Action::MoveToWorkspace(1),
Action::MoveToWorkspace(2),
@ -505,11 +501,9 @@ fn all_system_actions() -> &'static [Action] {
Action::MoveToWorkspace(7),
Action::MoveToWorkspace(8),
Action::MoveToWorkspace(9),
Action::NextOutput,
Action::NextWorkspace,
Action::Orientation(Orientation::Horizontal),
Action::Orientation(Orientation::Vertical),
Action::PreviousOutput,
Action::PreviousWorkspace,
Action::Resizing(ResizeDirection::Inwards),
Action::Resizing(ResizeDirection::Outwards),

View file

@ -59,9 +59,13 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::MoveWindowShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
}

View file

@ -59,9 +59,13 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::NavShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
}

View file

@ -59,9 +59,13 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::SystemShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
}

View file

@ -59,9 +59,13 @@ impl page::Page<crate::pages::Message> for Page {
.map(|el| el.map(crate::pages::Message::TilingShortcuts))
}
fn on_context_drawer_close(&mut self) -> Task<crate::pages::Message> {
self.model.on_context_drawer_close();
Task::none()
}
fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
Task::none()
}

View file

@ -88,10 +88,6 @@ impl Page {
match message {
Message::HostnameEdit(editing) => {
self.editing_device_name = editing;
if !editing {
return self.hostname_submit();
}
}
Message::HostnameInput(hostname) => {
@ -119,6 +115,7 @@ impl Page {
}
fn hostname_submit(&mut self) -> cosmic::app::Task<crate::app::Message> {
eprintln!("hostname submit");
if self.hostname_input == self.info.device_name {
return Task::none();
}
@ -182,6 +179,7 @@ fn device() -> Section<crate::pages::Message> {
)
.width(250)
.on_input(Message::HostnameInput)
.on_unfocus(Message::HostnameSubmit)
.on_submit(|_| Message::HostnameSubmit);
let device_name = settings::item::builder(&*desc[device])