use crate::compat::{String, Vec};
use crate::core::ObjectId;
use crate::signal::{ConnectionHandle, GenericSignal};
use alloc::sync::Arc;
use core::fmt;
#[derive(Clone)]
pub struct Action {
pub id: String,
pub text: String,
enabled: bool,
checkable: bool,
checked: bool,
triggered: GenericSignal,
toggled: crate::signal::Signal1<bool>,
enabled_changed: crate::signal::Signal1<bool>,
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Action")
.field("id", &self.id)
.field("text", &self.text)
.field("enabled", &self.enabled)
.field("checkable", &self.checkable)
.field("checked", &self.checked)
.field("triggered", &self.triggered.slot_count())
.field("toggled", &self.toggled.slot_count())
.field("enabled_changed", &self.enabled_changed.slot_count())
.finish()
}
}
impl Action {
pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
Self {
id: id.into(),
text: text.into(),
enabled: true,
checkable: false,
checked: false,
triggered: GenericSignal::new(),
toggled: crate::signal::Signal1::new(),
enabled_changed: crate::signal::Signal1::new(),
}
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn set_enabled(&mut self, enabled: bool) {
if self.enabled == enabled {
return;
}
self.enabled = enabled;
self.enabled_changed.emit(enabled);
}
pub fn is_checkable(&self) -> bool {
self.checkable
}
pub fn set_checkable(&mut self, checkable: bool) {
if self.checkable == checkable {
return;
}
self.checkable = checkable;
if !checkable {
self.set_checked(false);
}
}
pub fn is_checked(&self) -> bool {
self.checked
}
pub fn set_checked(&mut self, checked: bool) {
let normalized = if self.checkable { checked } else { false };
if self.checked == normalized {
return;
}
self.checked = normalized;
self.toggled.emit(self.checked);
}
pub fn connect_triggered<F>(&self, slot: F) -> ConnectionHandle
where
F: FnMut() + Send + Sync + 'static,
{
self.triggered.connect(slot)
}
pub fn connect_toggled<F>(&self, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<bool>) + Send + Sync + 'static,
{
self.toggled.connect(slot)
}
pub fn connect_enabled_changed<F>(&self, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<bool>) + Send + Sync + 'static,
{
self.enabled_changed.connect(slot)
}
pub fn trigger(&mut self) -> bool {
if !self.enabled {
return false;
}
if self.checkable {
self.set_checked(!self.checked);
}
self.triggered.emit();
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActionHostKind {
Menu,
Button,
ToolBar,
}
#[derive(Debug, Clone)]
pub struct ActionBinding {
pub action_id: String,
pub host_id: ObjectId,
pub kind: ActionHostKind,
}
pub(crate) fn normalize_shortcut(shortcut: &str) -> String {
let mut primary = false;
let mut alt = false;
let mut shift = false;
let mut meta = false;
let mut key: String = String::new();
for token in shortcut.split('+').map(|t| t.trim()).filter(|t| !t.is_empty()) {
let lower = token.to_lowercase();
match lower.as_str() {
"cmd" | "command" | "cmdorctrl" | "ctrl" | "control" | "primary" => primary = true,
"alt" | "option" => alt = true,
"shift" => shift = true,
"meta" | "win" | "super" => meta = true,
_ => key = lower,
}
}
let mut parts: Vec<String> = Vec::new();
if primary {
parts.push(String::from("primary"));
}
if alt {
parts.push(String::from("alt"));
}
if shift {
parts.push(String::from("shift"));
}
if meta {
parts.push(String::from("meta"));
}
if !key.is_empty() {
parts.push(key);
}
parts.join("+")
}
pub(crate) fn canonicalize_shortcut_type(shortcut: &crate::shortcut::Shortcut) -> String {
shortcut.format_shortcut().to_lowercase()
}