toml-config: add named actions
This commit is contained in:
parent
8552c5f1eb
commit
3100773ae0
15 changed files with 587 additions and 4 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -641,6 +641,7 @@ dependencies = [
|
||||||
"jay-config",
|
"jay-config",
|
||||||
"log",
|
"log",
|
||||||
"phf",
|
"phf",
|
||||||
|
"run-on-drop",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
@ -1092,6 +1093,12 @@ dependencies = [
|
||||||
"xmlparser",
|
"xmlparser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "run-on-drop"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c9afdcca45aed5b357d64e0d033f6400c07a668aa4f9b92d25d39a884889457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,26 @@
|
||||||
- Implement tablet-v2 version 2.
|
- Implement tablet-v2 version 2.
|
||||||
- Floating windows can now be pinned. A pinned floating window stays visible on
|
- Floating windows can now be pinned. A pinned floating window stays visible on
|
||||||
its output even when switching workspaces.
|
its output even when switching workspaces.
|
||||||
|
- The toml config can now contain named actions:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
actions.switch-to-1 = [
|
||||||
|
{ type = "show-workspace", name = "1" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-2" },
|
||||||
|
]
|
||||||
|
actions.switch-to-2 = [
|
||||||
|
{ type = "show-workspace", name = "2" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-3" },
|
||||||
|
]
|
||||||
|
actions.switch-to-3 = [
|
||||||
|
{ type = "show-workspace", name = "3" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-1" },
|
||||||
|
]
|
||||||
|
actions.switch-to-next = "$switch-to-1"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = "$switch-to-next"
|
||||||
|
```
|
||||||
|
|
||||||
# 1.10.0 (2025-04-22)
|
# 1.10.0 (2025-04-22)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ phf = { version = "0.11.2", features = ["macros"] }
|
||||||
indexmap = "2.2.5"
|
indexmap = "2.2.5"
|
||||||
bstr = { version = "1.9.1", default-features = false }
|
bstr = { version = "1.9.1", default-features = false }
|
||||||
ahash = "0.8.11"
|
ahash = "0.8.11"
|
||||||
|
run-on-drop = "1.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
simplelog = { version = "0.12.2", features = ["test"] }
|
simplelog = { version = "0.12.2", features = ["test"] }
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ use {
|
||||||
std::{
|
std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
|
rc::Rc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
|
@ -64,6 +65,7 @@ pub enum SimpleCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[expect(clippy::enum_variant_names)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
ConfigureConnector {
|
ConfigureConnector {
|
||||||
con: ConfigConnector,
|
con: ConfigConnector,
|
||||||
|
|
@ -133,6 +135,16 @@ pub enum Action {
|
||||||
SetRepeatRate {
|
SetRepeatRate {
|
||||||
rate: RepeatRate,
|
rate: RepeatRate,
|
||||||
},
|
},
|
||||||
|
DefineAction {
|
||||||
|
name: String,
|
||||||
|
action: Box<Action>,
|
||||||
|
},
|
||||||
|
UndefineAction {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
NamedAction {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -338,6 +350,12 @@ pub struct Shortcut {
|
||||||
pub latch: Option<Action>,
|
pub latch: Option<Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NamedAction {
|
||||||
|
pub name: Rc<String>,
|
||||||
|
pub action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub keymap: Option<ConfigKeymap>,
|
pub keymap: Option<ConfigKeymap>,
|
||||||
|
|
@ -371,6 +389,8 @@ pub struct Config {
|
||||||
pub xwayland: Option<Xwayland>,
|
pub xwayland: Option<Xwayland>,
|
||||||
pub color_management: Option<ColorManagement>,
|
pub color_management: Option<ColorManagement>,
|
||||||
pub float: Option<Float>,
|
pub float: Option<Float>,
|
||||||
|
pub named_actions: Vec<NamedAction>,
|
||||||
|
pub max_action_depth: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
|
mod actions;
|
||||||
mod color;
|
mod color;
|
||||||
pub mod color_management;
|
pub mod color_management;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,11 @@ pub struct ActionParser<'a>(pub &'a Context<'a>);
|
||||||
impl ActionParser<'_> {
|
impl ActionParser<'_> {
|
||||||
fn parse_simple_cmd(&self, span: Span, string: &str) -> ParseResult<Self> {
|
fn parse_simple_cmd(&self, span: Span, string: &str) -> ParseResult<Self> {
|
||||||
use {crate::config::SimpleCommand::*, jay_config::Direction::*};
|
use {crate::config::SimpleCommand::*, jay_config::Direction::*};
|
||||||
|
if let Some(name) = string.strip_prefix("$") {
|
||||||
|
return Ok(Action::NamedAction {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
let cmd = match string {
|
let cmd = match string {
|
||||||
"focus-left" => Focus(Left),
|
"focus-left" => Focus(Left),
|
||||||
"focus-down" => Focus(Down),
|
"focus-down" => Focus(Down),
|
||||||
|
|
@ -323,6 +328,28 @@ impl ActionParser<'_> {
|
||||||
.map_spanned_err(ActionParserError::RepeatRate)?;
|
.map_spanned_err(ActionParserError::RepeatRate)?;
|
||||||
Ok(Action::SetRepeatRate { rate })
|
Ok(Action::SetRepeatRate { rate })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_undefine_action(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
let (name,) = ext.extract((str("name"),))?;
|
||||||
|
Ok(Action::UndefineAction {
|
||||||
|
name: name.value.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_define_action(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
let (name, action) = ext.extract((str("name"), val("action")))?;
|
||||||
|
Ok(Action::DefineAction {
|
||||||
|
name: name.value.to_string(),
|
||||||
|
action: Box::new(action.parse(&mut ActionParser(self.0))?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_named_action(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
let (name,) = ext.extract((str("name"),))?;
|
||||||
|
Ok(Action::NamedAction {
|
||||||
|
name: name.value.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser for ActionParser<'_> {
|
impl Parser for ActionParser<'_> {
|
||||||
|
|
@ -374,6 +401,9 @@ impl Parser for ActionParser<'_> {
|
||||||
"configure-idle" => self.parse_configure_idle(&mut ext),
|
"configure-idle" => self.parse_configure_idle(&mut ext),
|
||||||
"move-to-output" => self.parse_move_to_output(&mut ext),
|
"move-to-output" => self.parse_move_to_output(&mut ext),
|
||||||
"set-repeat-rate" => self.parse_set_repeat_rate(&mut ext),
|
"set-repeat-rate" => self.parse_set_repeat_rate(&mut ext),
|
||||||
|
"define-action" => self.parse_define_action(&mut ext),
|
||||||
|
"undefine-action" => self.parse_undefine_action(&mut ext),
|
||||||
|
"named" => self.parse_named_action(&mut ext),
|
||||||
v => {
|
v => {
|
||||||
ext.ignore_unused();
|
ext.ignore_unused();
|
||||||
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
||||||
|
|
|
||||||
78
toml-config/src/config/parsers/actions.rs
Normal file
78
toml-config/src/config/parsers/actions.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
Action, NamedAction,
|
||||||
|
context::Context,
|
||||||
|
extractor::ExtractorError,
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
parsers::action::ActionParser,
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{Span, Spanned, SpannedExt},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
std::{collections::HashSet, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ActionsParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
ExtractorError(#[from] ExtractorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ActionsParser<'a, 'b> {
|
||||||
|
pub cx: &'a Context<'a>,
|
||||||
|
pub used_names: HashSet<Spanned<Rc<String>>>,
|
||||||
|
pub actions: &'b mut Vec<NamedAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser for ActionsParser<'_, '_> {
|
||||||
|
type Value = ();
|
||||||
|
type Error = ActionsParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
_span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
for (name, value) in table.iter() {
|
||||||
|
let Some(action) = parse_action(self.cx, &name.value, value) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let name = Rc::new(name.value.clone()).spanned(name.span);
|
||||||
|
log_used(self.cx, &mut self.used_names, name.clone());
|
||||||
|
self.actions.push(NamedAction {
|
||||||
|
name: name.value,
|
||||||
|
action,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_action(cx: &Context<'_>, name: &str, value: &Spanned<Value>) -> Option<Action> {
|
||||||
|
match value.parse(&mut ActionParser(cx)) {
|
||||||
|
Ok(a) => Some(a),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse action for name {name}: {}", cx.error(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_used(cx: &Context<'_>, used: &mut HashSet<Spanned<Rc<String>>>, key: Spanned<Rc<String>>) {
|
||||||
|
if let Some(prev) = used.get(&key) {
|
||||||
|
log::warn!(
|
||||||
|
"Duplicate actions overrides previous definition: {}",
|
||||||
|
cx.error3(key.span)
|
||||||
|
);
|
||||||
|
log::info!("Previous definition here: {}", cx.error3(prev.span));
|
||||||
|
}
|
||||||
|
used.insert(key);
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,11 @@ use {
|
||||||
config::{
|
config::{
|
||||||
Action, Config, Libei, Theme, UiDrag,
|
Action, Config, Libei, Theme, UiDrag,
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{Extractor, ExtractorError, arr, bol, opt, recover, str, val},
|
extractor::{Extractor, ExtractorError, arr, bol, int, opt, recover, str, val},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
parsers::{
|
parsers::{
|
||||||
action::ActionParser,
|
action::ActionParser,
|
||||||
|
actions::ActionsParser,
|
||||||
color_management::ColorManagementParser,
|
color_management::ColorManagementParser,
|
||||||
connector::ConnectorsParser,
|
connector::ConnectorsParser,
|
||||||
drm_device::DrmDevicesParser,
|
drm_device::DrmDevicesParser,
|
||||||
|
|
@ -119,7 +120,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
ui_drag_val,
|
ui_drag_val,
|
||||||
xwayland_val,
|
xwayland_val,
|
||||||
),
|
),
|
||||||
(color_management_val, float_val),
|
(color_management_val, float_val, actions_val, max_action_depth_val),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(val("keymap")),
|
opt(val("keymap")),
|
||||||
|
|
@ -157,7 +158,12 @@ impl Parser for ConfigParser<'_> {
|
||||||
opt(val("ui-drag")),
|
opt(val("ui-drag")),
|
||||||
opt(val("xwayland")),
|
opt(val("xwayland")),
|
||||||
),
|
),
|
||||||
(opt(val("color-management")), opt(val("float"))),
|
(
|
||||||
|
opt(val("color-management")),
|
||||||
|
opt(val("float")),
|
||||||
|
opt(val("actions")),
|
||||||
|
recover(opt(int("max-action-depth"))),
|
||||||
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
if let Some(value) = keymap_val {
|
if let Some(value) = keymap_val {
|
||||||
|
|
@ -391,6 +397,28 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut named_actions = vec![];
|
||||||
|
if let Some(value) = actions_val {
|
||||||
|
let mut parser = ActionsParser {
|
||||||
|
cx: self.0,
|
||||||
|
used_names: Default::default(),
|
||||||
|
actions: &mut named_actions,
|
||||||
|
};
|
||||||
|
if let Err(e) = value.parse(&mut parser) {
|
||||||
|
log::warn!("Could not parse named actions: {}", self.0.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut max_action_depth = 16;
|
||||||
|
if let Some(mut value) = max_action_depth_val {
|
||||||
|
if value.value < 0 {
|
||||||
|
log::warn!(
|
||||||
|
"Max action depth should not be negative: {}",
|
||||||
|
self.0.error3(value.span)
|
||||||
|
);
|
||||||
|
value.value = 0;
|
||||||
|
}
|
||||||
|
max_action_depth = value.value as _;
|
||||||
|
}
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
keymap,
|
keymap,
|
||||||
repeat_rate,
|
repeat_rate,
|
||||||
|
|
@ -423,6 +451,8 @@ impl Parser for ConfigParser<'_> {
|
||||||
xwayland,
|
xwayland,
|
||||||
color_management,
|
color_management,
|
||||||
float,
|
float,
|
||||||
|
named_actions,
|
||||||
|
max_action_depth,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,14 @@ use {
|
||||||
},
|
},
|
||||||
xwayland::set_x_scaling_mode,
|
xwayland::set_x_scaling_mode,
|
||||||
},
|
},
|
||||||
std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc, time::Duration},
|
run_on_drop::on_drop,
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
io::ErrorKind,
|
||||||
|
path::PathBuf,
|
||||||
|
rc::Rc,
|
||||||
|
time::Duration,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn default_seat() -> Seat {
|
fn default_seat() -> Seat {
|
||||||
|
|
@ -225,6 +232,40 @@ impl Action {
|
||||||
Action::SetRepeatRate { rate } => {
|
Action::SetRepeatRate { rate } => {
|
||||||
B::new(move || s.set_repeat_rate(rate.rate, rate.delay))
|
B::new(move || s.set_repeat_rate(rate.rate, rate.delay))
|
||||||
}
|
}
|
||||||
|
Action::DefineAction { name, action } => {
|
||||||
|
let state = state.clone();
|
||||||
|
let action = action.into_rc_fn(&state);
|
||||||
|
let name = Rc::new(name);
|
||||||
|
B::new(move || {
|
||||||
|
state
|
||||||
|
.actions
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(name.clone(), action.clone());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Action::UndefineAction { name } => {
|
||||||
|
let state = state.clone();
|
||||||
|
B::new(move || {
|
||||||
|
state.actions.borrow_mut().remove(&name);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Action::NamedAction { name } => {
|
||||||
|
let state = state.clone();
|
||||||
|
B::new(move || {
|
||||||
|
let depth = state.action_depth.get();
|
||||||
|
if depth >= state.action_depth_max {
|
||||||
|
log::error!("Maximum action depth reached");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.action_depth.set(depth + 1);
|
||||||
|
let _reset = on_drop(|| state.action_depth.set(depth));
|
||||||
|
let Some(action) = state.actions.borrow().get(&name).cloned() else {
|
||||||
|
log::error!("There is no action named {name}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
action();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -607,6 +648,7 @@ impl Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::type_complexity)]
|
||||||
struct State {
|
struct State {
|
||||||
outputs: AHashMap<String, OutputMatch>,
|
outputs: AHashMap<String, OutputMatch>,
|
||||||
drm_devices: AHashMap<String, DrmDeviceMatch>,
|
drm_devices: AHashMap<String, DrmDeviceMatch>,
|
||||||
|
|
@ -617,6 +659,10 @@ struct State {
|
||||||
io_maps: Vec<(InputMatch, OutputMatch)>,
|
io_maps: Vec<(InputMatch, OutputMatch)>,
|
||||||
io_inputs: RefCell<AHashMap<InputDevice, Vec<bool>>>,
|
io_inputs: RefCell<AHashMap<InputDevice, Vec<bool>>>,
|
||||||
io_outputs: RefCell<AHashMap<Connector, Vec<bool>>>,
|
io_outputs: RefCell<AHashMap<Connector, Vec<bool>>>,
|
||||||
|
|
||||||
|
action_depth_max: u64,
|
||||||
|
action_depth: Cell<u64>,
|
||||||
|
actions: RefCell<AHashMap<Rc<String>, Rc<dyn Fn()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for State {
|
impl Drop for State {
|
||||||
|
|
@ -914,8 +960,15 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
||||||
io_maps,
|
io_maps,
|
||||||
io_inputs: Default::default(),
|
io_inputs: Default::default(),
|
||||||
io_outputs: Default::default(),
|
io_outputs: Default::default(),
|
||||||
|
action_depth_max: config.max_action_depth,
|
||||||
|
action_depth: Cell::new(0),
|
||||||
|
actions: Default::default(),
|
||||||
});
|
});
|
||||||
state.set_status(&config.status);
|
state.set_status(&config.status);
|
||||||
|
for a in config.named_actions {
|
||||||
|
let action = a.action.into_rc_fn(&state);
|
||||||
|
state.actions.borrow_mut().insert(a.name, action);
|
||||||
|
}
|
||||||
let mut switch_actions = vec![];
|
let mut switch_actions = vec![];
|
||||||
for input in &mut config.inputs {
|
for input in &mut config.inputs {
|
||||||
let mut actions = AHashMap::new();
|
let mut actions = AHashMap::new();
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@
|
||||||
"description": "The value should be the name of a `simple` action. See the description of that\nvariant for more details.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
"description": "The value should be the name of a `simple` action. See the description of that\nvariant for more details.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
||||||
"$ref": "#/$defs/SimpleActionName"
|
"$ref": "#/$defs/SimpleActionName"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The value should be the name of a `named` action, prefixed with the `$` character.\n\nThis is the same as using the `named` action with the `$` removed.\n\n- Example:\n\n ```toml\n [actions]\n q = \"quit\"\n\n [shortcuts]\n alt-q = \"$q\"\n ```\n",
|
||||||
|
"pattern": "^\\$.*$"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "A list of actions to execute in sequence.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = [\n { type = \"exec\", exec = [\"notify-send\", \"exiting\"] },\n \"quit\",\n ]\n ```\n",
|
"description": "A list of actions to execute in sequence.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = [\n { type = \"exec\", exec = [\"notify-send\", \"exiting\"] },\n \"quit\",\n ]\n ```\n",
|
||||||
|
|
@ -46,6 +51,23 @@
|
||||||
"cmd"
|
"cmd"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "A named action that was defined via the top-level `actions` table or a\n`define-action` action. These are usually written as plain strings with a `$`\nprefix.\n\n- Example 1:\n\n ```toml\n [actions]\n my-action = \"quit\"\n\n [shortcuts]\n alt-q = { type = \"named\", name = \"my-action\" }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n alt-q = [\n { type = \"define-action\", name = \"my-action\", action = \"quit\" },\n { type = \"named\", name = \"my-action\" },\n ]\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "named"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The named action to execute."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "A list of actions to execute in sequence. These are usually written as plain\narrays instead.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n alt-q = { type = \"multi\", actions = [\"quit\", \"quit\"] }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n alt-q = [\"quit\", \"quit\"]\n ```\n",
|
"description": "A list of actions to execute in sequence. These are usually written as plain\narrays instead.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n alt-q = { type = \"multi\", actions = [\"quit\", \"quit\"] }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n alt-q = [\"quit\", \"quit\"]\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -418,6 +440,45 @@
|
||||||
"type",
|
"type",
|
||||||
"dev"
|
"dev"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Defines a name for an action. Usually you would define these by using the\ntop-level `actions` table. This action can be used to re-define actions.\n\n- Example:\n\n ```toml\n [actions]\n a1 = \"quit\"\n a2 = \"$a1\"\n\n [shortcuts]\n alt-q = [\n { type = \"define-action\", name = \"a2\", action = [] },\n \"$2\", # does nothing\n ]\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "define-action"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the action."
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"description": "The action to execute.",
|
||||||
|
"$ref": "#/$defs/Action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"name",
|
||||||
|
"action"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Removes a named action.\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "undefine-action"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the action."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"name"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -640,6 +701,19 @@
|
||||||
"float": {
|
"float": {
|
||||||
"description": "Configures the settings of floating windows.\n\n- Example:\n\n ```toml\n [float]\n show-pin-icon = true\n ```\n",
|
"description": "Configures the settings of floating windows.\n\n- Example:\n\n ```toml\n [float]\n show-pin-icon = true\n ```\n",
|
||||||
"$ref": "#/$defs/Float"
|
"$ref": "#/$defs/Float"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"description": "Named actions.\n\nNamed actions can be used everywhere an action can be used. This can be used to\navoid repeating the same action multiple times.\n\n- Example:\n\n ```toml\n actions.switch-to-1 = [\n { type = \"show-workspace\", name = \"1\" },\n { type = \"define-action\", name = \"switch-to-next\", action = \"$switch-to-2\" },\n ]\n actions.switch-to-2 = [\n { type = \"show-workspace\", name = \"2\" },\n { type = \"define-action\", name = \"switch-to-next\", action = \"$switch-to-3\" },\n ]\n actions.switch-to-3 = [\n { type = \"show-workspace\", name = \"3\" },\n { type = \"define-action\", name = \"switch-to-next\", action = \"$switch-to-1\" },\n ]\n actions.switch-to-next = \"$switch-to-1\"\n\n [shortcuts]\n alt-x = \"$switch-to-next\"\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"description": "",
|
||||||
|
"$ref": "#/$defs/Action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"max-action-depth": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The maximum call depth of named actions. This setting prevents infinite recursion\nwhen using named actions. Setting this value to 0 or less disables named actions\ncompletely. The default is `16`.\n",
|
||||||
|
"minimum": 0.0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,24 @@ variant for more details.
|
||||||
|
|
||||||
The value should be a [SimpleActionName](#types-SimpleActionName).
|
The value should be a [SimpleActionName](#types-SimpleActionName).
|
||||||
|
|
||||||
|
#### A string
|
||||||
|
|
||||||
|
The value should be the name of a `named` action, prefixed with the `$` character.
|
||||||
|
|
||||||
|
This is the same as using the `named` action with the `$` removed.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
q = "quit"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = "$q"
|
||||||
|
```
|
||||||
|
|
||||||
|
The string should match the following regular expression: `^\$.*$`
|
||||||
|
|
||||||
#### An array
|
#### An array
|
||||||
|
|
||||||
A list of actions to execute in sequence.
|
A list of actions to execute in sequence.
|
||||||
|
|
@ -106,6 +124,40 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
The value of this field should be a [SimpleActionName](#types-SimpleActionName).
|
The value of this field should be a [SimpleActionName](#types-SimpleActionName).
|
||||||
|
|
||||||
|
- `named`:
|
||||||
|
|
||||||
|
A named action that was defined via the top-level `actions` table or a
|
||||||
|
`define-action` action. These are usually written as plain strings with a `$`
|
||||||
|
prefix.
|
||||||
|
|
||||||
|
- Example 1:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
my-action = "quit"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = { type = "named", name = "my-action" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Example 2:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = [
|
||||||
|
{ type = "define-action", name = "my-action", action = "quit" },
|
||||||
|
{ type = "named", name = "my-action" },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `name` (required):
|
||||||
|
|
||||||
|
The named action to execute.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
- `multi`:
|
- `multi`:
|
||||||
|
|
||||||
A list of actions to execute in sequence. These are usually written as plain
|
A list of actions to execute in sequence. These are usually written as plain
|
||||||
|
|
@ -574,6 +626,51 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
The value of this field should be a [DrmDeviceMatch](#types-DrmDeviceMatch).
|
The value of this field should be a [DrmDeviceMatch](#types-DrmDeviceMatch).
|
||||||
|
|
||||||
|
- `define-action`:
|
||||||
|
|
||||||
|
Defines a name for an action. Usually you would define these by using the
|
||||||
|
top-level `actions` table. This action can be used to re-define actions.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
a1 = "quit"
|
||||||
|
a2 = "$a1"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = [
|
||||||
|
{ type = "define-action", name = "a2", action = [] },
|
||||||
|
"$2", # does nothing
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `name` (required):
|
||||||
|
|
||||||
|
The name of the action.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `action` (required):
|
||||||
|
|
||||||
|
The action to execute.
|
||||||
|
|
||||||
|
The value of this field should be a [Action](#types-Action).
|
||||||
|
|
||||||
|
- `undefine-action`:
|
||||||
|
|
||||||
|
Removes a named action.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `name` (required):
|
||||||
|
|
||||||
|
The name of the action.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Brightness"></a>
|
<a name="types-Brightness"></a>
|
||||||
### `Brightness`
|
### `Brightness`
|
||||||
|
|
@ -1278,6 +1375,48 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [Float](#types-Float).
|
The value of this field should be a [Float](#types-Float).
|
||||||
|
|
||||||
|
- `actions` (optional):
|
||||||
|
|
||||||
|
Named actions.
|
||||||
|
|
||||||
|
Named actions can be used everywhere an action can be used. This can be used to
|
||||||
|
avoid repeating the same action multiple times.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
actions.switch-to-1 = [
|
||||||
|
{ type = "show-workspace", name = "1" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-2" },
|
||||||
|
]
|
||||||
|
actions.switch-to-2 = [
|
||||||
|
{ type = "show-workspace", name = "2" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-3" },
|
||||||
|
]
|
||||||
|
actions.switch-to-3 = [
|
||||||
|
{ type = "show-workspace", name = "3" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-1" },
|
||||||
|
]
|
||||||
|
actions.switch-to-next = "$switch-to-1"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = "$switch-to-next"
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of this field should be a table whose values are [Actions](#types-Action).
|
||||||
|
|
||||||
|
- `max-action-depth` (optional):
|
||||||
|
|
||||||
|
The maximum call depth of named actions. This setting prevents infinite recursion
|
||||||
|
when using named actions. Setting this value to 0 or less disables named actions
|
||||||
|
completely. The default is `16`.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Connector"></a>
|
<a name="types-Connector"></a>
|
||||||
### `Connector`
|
### `Connector`
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,22 @@ Action:
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-q = "quit"
|
alt-q = "quit"
|
||||||
```
|
```
|
||||||
|
- kind: string
|
||||||
|
pattern: "^\\$.*$"
|
||||||
|
description: |
|
||||||
|
The value should be the name of a `named` action, prefixed with the `$` character.
|
||||||
|
|
||||||
|
This is the same as using the `named` action with the `$` removed.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
q = "quit"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = "$q"
|
||||||
|
```
|
||||||
- kind: array
|
- kind: array
|
||||||
items:
|
items:
|
||||||
ref: Action
|
ref: Action
|
||||||
|
|
@ -130,6 +146,36 @@ Action:
|
||||||
description: The simple action to execute.
|
description: The simple action to execute.
|
||||||
required: true
|
required: true
|
||||||
ref: SimpleActionName
|
ref: SimpleActionName
|
||||||
|
named:
|
||||||
|
description: |
|
||||||
|
A named action that was defined via the top-level `actions` table or a
|
||||||
|
`define-action` action. These are usually written as plain strings with a `$`
|
||||||
|
prefix.
|
||||||
|
|
||||||
|
- Example 1:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
my-action = "quit"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = { type = "named", name = "my-action" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Example 2:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = [
|
||||||
|
{ type = "define-action", name = "my-action", action = "quit" },
|
||||||
|
{ type = "named", name = "my-action" },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
kind: string
|
||||||
|
description: The named action to execute.
|
||||||
|
required: true
|
||||||
multi:
|
multi:
|
||||||
description: |
|
description: |
|
||||||
A list of actions to execute in sequence. These are usually written as plain
|
A list of actions to execute in sequence. These are usually written as plain
|
||||||
|
|
@ -521,6 +567,41 @@ Action:
|
||||||
The first matching device is used.
|
The first matching device is used.
|
||||||
required: true
|
required: true
|
||||||
ref: DrmDeviceMatch
|
ref: DrmDeviceMatch
|
||||||
|
define-action:
|
||||||
|
description: |
|
||||||
|
Defines a name for an action. Usually you would define these by using the
|
||||||
|
top-level `actions` table. This action can be used to re-define actions.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[actions]
|
||||||
|
a1 = "quit"
|
||||||
|
a2 = "$a1"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-q = [
|
||||||
|
{ type = "define-action", name = "a2", action = [] },
|
||||||
|
"$2", # does nothing
|
||||||
|
]
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
kind: string
|
||||||
|
description: The name of the action.
|
||||||
|
required: true
|
||||||
|
action:
|
||||||
|
description: The action to execute.
|
||||||
|
required: true
|
||||||
|
ref: Action
|
||||||
|
undefine-action:
|
||||||
|
description: |
|
||||||
|
Removes a named action.
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
kind: string
|
||||||
|
description: The name of the action.
|
||||||
|
required: true
|
||||||
|
|
||||||
|
|
||||||
Exec:
|
Exec:
|
||||||
|
|
@ -2350,6 +2431,46 @@ Config:
|
||||||
[float]
|
[float]
|
||||||
show-pin-icon = true
|
show-pin-icon = true
|
||||||
```
|
```
|
||||||
|
actions:
|
||||||
|
kind: map
|
||||||
|
values:
|
||||||
|
ref: Action
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Named actions.
|
||||||
|
|
||||||
|
Named actions can be used everywhere an action can be used. This can be used to
|
||||||
|
avoid repeating the same action multiple times.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
actions.switch-to-1 = [
|
||||||
|
{ type = "show-workspace", name = "1" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-2" },
|
||||||
|
]
|
||||||
|
actions.switch-to-2 = [
|
||||||
|
{ type = "show-workspace", name = "2" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-3" },
|
||||||
|
]
|
||||||
|
actions.switch-to-3 = [
|
||||||
|
{ type = "show-workspace", name = "3" },
|
||||||
|
{ type = "define-action", name = "switch-to-next", action = "$switch-to-1" },
|
||||||
|
]
|
||||||
|
actions.switch-to-next = "$switch-to-1"
|
||||||
|
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = "$switch-to-next"
|
||||||
|
```
|
||||||
|
max-action-depth:
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
minimum: 0
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
The maximum call depth of named actions. This setting prevents infinite recursion
|
||||||
|
when using named actions. Setting this value to 0 or less disables named actions
|
||||||
|
completely. The default is `16`.
|
||||||
|
|
||||||
|
|
||||||
Idle:
|
Idle:
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,8 @@ fn create_string_spec(description: &str, spec: &StringSpec) -> Value {
|
||||||
if let Some(values) = &spec.values {
|
if let Some(values) = &spec.values {
|
||||||
let strings: Vec<_> = values.iter().map(|v| &v.value.value).collect();
|
let strings: Vec<_> = values.iter().map(|v| &v.value.value).collect();
|
||||||
res.insert("enum".into(), json!(strings));
|
res.insert("enum".into(), json!(strings));
|
||||||
|
} else if let Some(pattern) = &spec.pattern {
|
||||||
|
res.insert("pattern".into(), json!(pattern));
|
||||||
}
|
}
|
||||||
res.into()
|
res.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,12 @@ fn write_string_spec(buf: &mut Vec<u8>, spec: &StringSpec, pad: &str) -> Result<
|
||||||
writeln!(buf)?;
|
writeln!(buf)?;
|
||||||
}
|
}
|
||||||
writeln!(buf)?;
|
writeln!(buf)?;
|
||||||
|
} else if let Some(pattern) = &spec.pattern {
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"{pad}The string should match the following regular expression: `{pattern}`"
|
||||||
|
)?;
|
||||||
|
writeln!(buf)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ pub enum RefOrSpec<T> {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct StringSpec {
|
pub struct StringSpec {
|
||||||
|
pub pattern: Option<String>,
|
||||||
pub values: Option<Vec<Described<StringSpecValue>>>,
|
pub values: Option<Vec<Described<StringSpecValue>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue