config: add floating window criteria
This commit is contained in:
parent
da64166e82
commit
8bb8b2a649
14 changed files with 78 additions and 14 deletions
|
|
@ -110,6 +110,7 @@ pub enum WindowCriterionIpc {
|
||||||
},
|
},
|
||||||
Types(WindowType),
|
Types(WindowType),
|
||||||
Client(ClientMatcher),
|
Client(ClientMatcher),
|
||||||
|
Floating,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -1657,6 +1657,7 @@ impl ConfigClient {
|
||||||
WindowCriterion::TitleRegex(t) => string!(t, Title, true),
|
WindowCriterion::TitleRegex(t) => string!(t, Title, true),
|
||||||
WindowCriterion::AppId(t) => string!(t, AppId, false),
|
WindowCriterion::AppId(t) => string!(t, AppId, false),
|
||||||
WindowCriterion::AppIdRegex(t) => string!(t, AppId, true),
|
WindowCriterion::AppIdRegex(t) => string!(t, AppId, true),
|
||||||
|
WindowCriterion::Floating => WindowCriterionIpc::Floating,
|
||||||
};
|
};
|
||||||
let res = self.send_with_response(&ClientMessage::CreateWindowMatcher { criterion });
|
let res = self.send_with_response(&ClientMessage::CreateWindowMatcher { criterion });
|
||||||
get_response!(
|
get_response!(
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,8 @@ pub enum WindowCriterion<'a> {
|
||||||
AppId(&'a str),
|
AppId(&'a str),
|
||||||
/// Matches the app-id of the window with a regular expression.
|
/// Matches the app-id of the window with a regular expression.
|
||||||
AppIdRegex(&'a str),
|
AppIdRegex(&'a str),
|
||||||
|
/// Matches if the window is floating.
|
||||||
|
Floating,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowCriterion<'_> {
|
impl WindowCriterion<'_> {
|
||||||
|
|
|
||||||
|
|
@ -1997,6 +1997,7 @@ impl ConfigProxyHandler {
|
||||||
self.state.cl_matcher_manager.rematch_all(&self.state);
|
self.state.cl_matcher_manager.rematch_all(&self.state);
|
||||||
mgr.client(&self.state, &self.get_client_matcher(*c)?.node)
|
mgr.client(&self.state, &self.get_client_matcher(*c)?.node)
|
||||||
}
|
}
|
||||||
|
WindowCriterionIpc::Floating => mgr.floating(),
|
||||||
};
|
};
|
||||||
let cached = Rc::new(CachedCriterion {
|
let cached = Rc::new(CachedCriterion {
|
||||||
crit: criterion.clone(),
|
crit: criterion.clone(),
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@ use {
|
||||||
CritDestroyListener, CritLiteralOrRegex, CritMatcherId, CritMatcherIds, CritMgrExt,
|
CritDestroyListener, CritLiteralOrRegex, CritMatcherId, CritMatcherIds, CritMgrExt,
|
||||||
CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
||||||
clm::ClmUpstreamNode,
|
clm::ClmUpstreamNode,
|
||||||
crit_graph::{CritMgr, CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
crit_graph::{
|
||||||
|
CritMgr, CritRoot, CritRootFixed, CritTarget, CritTargetOwner, WeakCritTargetOwner,
|
||||||
|
},
|
||||||
crit_leaf::{CritLeafEvent, CritLeafMatcher},
|
crit_leaf::{CritLeafEvent, CritLeafMatcher},
|
||||||
crit_matchers::critm_constant::CritMatchConstant,
|
crit_matchers::critm_constant::CritMatchConstant,
|
||||||
tlm::tlm_matchers::{
|
tlm::tlm_matchers::{
|
||||||
tlmm_client::TlmMatchClient,
|
tlmm_client::TlmMatchClient,
|
||||||
|
tlmm_floating::TlmMatchFloating,
|
||||||
tlmm_kind::TlmMatchKind,
|
tlmm_kind::TlmMatchKind,
|
||||||
tlmm_string::{TlmMatchAppId, TlmMatchTitle},
|
tlmm_string::{TlmMatchAppId, TlmMatchTitle},
|
||||||
},
|
},
|
||||||
|
|
@ -23,7 +26,11 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jay_config::window::WindowType,
|
jay_config::window::WindowType,
|
||||||
std::rc::{Rc, Weak},
|
linearize::static_map,
|
||||||
|
std::{
|
||||||
|
marker::PhantomData,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
|
@ -32,6 +39,7 @@ bitflags! {
|
||||||
TL_CHANGED_NEW = 1 << 1,
|
TL_CHANGED_NEW = 1 << 1,
|
||||||
TL_CHANGED_TITLE = 1 << 2,
|
TL_CHANGED_TITLE = 1 << 2,
|
||||||
TL_CHANGED_APP_ID = 1 << 3,
|
TL_CHANGED_APP_ID = 1 << 3,
|
||||||
|
TL_CHANGED_FLOATING = 1 << 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
type TlmFixedRootMatcher<T> = FixedRootMatcher<ToplevelData, T>;
|
type TlmFixedRootMatcher<T> = FixedRootMatcher<ToplevelData, T>;
|
||||||
|
|
@ -41,6 +49,7 @@ pub struct TlMatcherManager {
|
||||||
changes: AsyncQueue<Rc<dyn ToplevelNode>>,
|
changes: AsyncQueue<Rc<dyn ToplevelNode>>,
|
||||||
leaf_events: Rc<AsyncQueue<CritLeafEvent<ToplevelData>>>,
|
leaf_events: Rc<AsyncQueue<CritLeafEvent<ToplevelData>>>,
|
||||||
constant: TlmFixedRootMatcher<CritMatchConstant<ToplevelData>>,
|
constant: TlmFixedRootMatcher<CritMatchConstant<ToplevelData>>,
|
||||||
|
floating: TlmFixedRootMatcher<TlmMatchFloating>,
|
||||||
matchers: Rc<RootMatchers>,
|
matchers: Rc<RootMatchers>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,8 +87,20 @@ pub type TlmLeafMatcher = CritLeafMatcher<ToplevelData>;
|
||||||
impl TlMatcherManager {
|
impl TlMatcherManager {
|
||||||
pub fn new(ids: &Rc<CritMatcherIds>) -> Self {
|
pub fn new(ids: &Rc<CritMatcherIds>) -> Self {
|
||||||
let matchers = Rc::new(RootMatchers::default());
|
let matchers = Rc::new(RootMatchers::default());
|
||||||
|
macro_rules! bool {
|
||||||
|
($name:ident) => {{
|
||||||
|
static_map! {
|
||||||
|
v => CritRoot::new(
|
||||||
|
&matchers,
|
||||||
|
ids.next(),
|
||||||
|
CritRootFixed($name(v), PhantomData),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
constant: CritMatchConstant::create(&matchers, ids),
|
constant: CritMatchConstant::create(&matchers, ids),
|
||||||
|
floating: bool!(TlmMatchFloating),
|
||||||
changes: Default::default(),
|
changes: Default::default(),
|
||||||
leaf_events: Default::default(),
|
leaf_events: Default::default(),
|
||||||
ids: ids.clone(),
|
ids: ids.clone(),
|
||||||
|
|
@ -108,7 +129,6 @@ impl TlMatcherManager {
|
||||||
if change.contains(TL_CHANGED_DESTROYED) && data.destroyed.is_not_empty() {
|
if change.contains(TL_CHANGED_DESTROYED) && data.destroyed.is_not_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! fixed {
|
macro_rules! fixed {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if self.$name[false].has_downstream() || self.$name[true].has_downstream() {
|
if self.$name[false].has_downstream() || self.$name[true].has_downstream() {
|
||||||
|
|
@ -138,7 +158,6 @@ impl TlMatcherManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! fixed_conditional {
|
macro_rules! fixed_conditional {
|
||||||
($change:expr, $field:ident) => {
|
($change:expr, $field:ident) => {
|
||||||
if change.contains($change) {
|
if change.contains($change) {
|
||||||
|
|
@ -148,6 +167,7 @@ impl TlMatcherManager {
|
||||||
}
|
}
|
||||||
conditional!(TL_CHANGED_TITLE, title);
|
conditional!(TL_CHANGED_TITLE, title);
|
||||||
conditional!(TL_CHANGED_APP_ID, app_id);
|
conditional!(TL_CHANGED_APP_ID, app_id);
|
||||||
|
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,7 +197,6 @@ impl TlMatcherManager {
|
||||||
.filter_map(|m| m.upgrade())
|
.filter_map(|m| m.upgrade())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! fixed {
|
macro_rules! fixed {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
self.$name[false].handle(data);
|
self.$name[false].handle(data);
|
||||||
|
|
@ -206,7 +225,6 @@ impl TlMatcherManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! fixed_conditional {
|
macro_rules! fixed_conditional {
|
||||||
($change:expr, $field:ident) => {
|
($change:expr, $field:ident) => {
|
||||||
if changed.contains($change) {
|
if changed.contains($change) {
|
||||||
|
|
@ -216,6 +234,7 @@ impl TlMatcherManager {
|
||||||
}
|
}
|
||||||
conditional!(TL_CHANGED_TITLE, title);
|
conditional!(TL_CHANGED_TITLE, title);
|
||||||
conditional!(TL_CHANGED_APP_ID, app_id);
|
conditional!(TL_CHANGED_APP_ID, app_id);
|
||||||
|
fixed_conditional!(TL_CHANGED_FLOATING, floating);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title(&self, string: CritLiteralOrRegex) -> Rc<TlmUpstreamNode> {
|
pub fn title(&self, string: CritLiteralOrRegex) -> Rc<TlmUpstreamNode> {
|
||||||
|
|
@ -226,6 +245,10 @@ impl TlMatcherManager {
|
||||||
self.root(TlmMatchAppId::new(string))
|
self.root(TlmMatchAppId::new(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn floating(&self) -> Rc<TlmUpstreamNode> {
|
||||||
|
self.floating[true].clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kind(&self, kind: WindowType) -> Rc<TlmUpstreamNode> {
|
pub fn kind(&self, kind: WindowType) -> Rc<TlmUpstreamNode> {
|
||||||
self.root(TlmMatchKind::new(kind))
|
self.root(TlmMatchKind::new(kind))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! fixed_root_criterion {
|
macro_rules! fixed_root_criterion {
|
||||||
($ty:ty, $field:ident) => {
|
($ty:ty, $field:ident) => {
|
||||||
impl crate::criteria::crit_graph::CritFixedRootCriterionBase<crate::tree::ToplevelData>
|
impl crate::criteria::crit_graph::CritFixedRootCriterionBase<crate::tree::ToplevelData>
|
||||||
|
|
@ -19,5 +18,6 @@ macro_rules! fixed_root_criterion {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod tlmm_client;
|
pub mod tlmm_client;
|
||||||
|
pub mod tlmm_floating;
|
||||||
pub mod tlmm_kind;
|
pub mod tlmm_kind;
|
||||||
pub mod tlmm_string;
|
pub mod tlmm_string;
|
||||||
|
|
|
||||||
11
src/criteria/tlm/tlm_matchers/tlmm_floating.rs
Normal file
11
src/criteria/tlm/tlm_matchers/tlmm_floating.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::{criteria::crit_graph::CritFixedRootCriterion, tree::ToplevelData};
|
||||||
|
|
||||||
|
pub struct TlmMatchFloating(pub bool);
|
||||||
|
|
||||||
|
fixed_root_criterion!(TlmMatchFloating, floating);
|
||||||
|
|
||||||
|
impl CritFixedRootCriterion<ToplevelData> for TlmMatchFloating {
|
||||||
|
fn matches(&self, data: &ToplevelData) -> bool {
|
||||||
|
data.is_floating.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,8 @@ use {
|
||||||
criteria::{
|
criteria::{
|
||||||
CritDestroyListener, CritMatcherId,
|
CritDestroyListener, CritMatcherId,
|
||||||
tlm::{
|
tlm::{
|
||||||
TL_CHANGED_APP_ID, TL_CHANGED_DESTROYED, TL_CHANGED_NEW, TL_CHANGED_TITLE,
|
TL_CHANGED_APP_ID, TL_CHANGED_DESTROYED, TL_CHANGED_FLOATING, TL_CHANGED_NEW,
|
||||||
TlMatcherChange,
|
TL_CHANGED_TITLE, TlMatcherChange,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -104,7 +104,12 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
if parent_was_none {
|
if parent_was_none {
|
||||||
data.property_changed(TL_CHANGED_NEW);
|
data.property_changed(TL_CHANGED_NEW);
|
||||||
}
|
}
|
||||||
data.is_floating.set(parent.node_is_float());
|
let was_floating = data.is_floating.get();
|
||||||
|
let is_floating = parent.node_is_float();
|
||||||
|
if was_floating != is_floating {
|
||||||
|
data.property_changed(TL_CHANGED_FLOATING);
|
||||||
|
}
|
||||||
|
data.is_floating.set(is_floating);
|
||||||
self.tl_set_workspace(&parent.cnode_workspace());
|
self.tl_set_workspace(&parent.cnode_workspace());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,7 @@ pub struct WindowMatch {
|
||||||
pub title_regex: Option<String>,
|
pub title_regex: Option<String>,
|
||||||
pub app_id: Option<String>,
|
pub app_id: Option<String>,
|
||||||
pub app_id_regex: Option<String>,
|
pub app_id_regex: Option<String>,
|
||||||
|
pub floating: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
config::{
|
config::{
|
||||||
GenericMatch, MatchExactly, WindowMatch,
|
GenericMatch, MatchExactly, WindowMatch,
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{Extractor, ExtractorError, arr, n32, opt, str, val},
|
extractor::{Extractor, ExtractorError, arr, bol, n32, opt, str, val},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
parsers::{
|
parsers::{
|
||||||
client_match::{ClientMatchParser, ClientMatchParserError},
|
client_match::{ClientMatchParser, ClientMatchParserError},
|
||||||
|
|
@ -56,7 +56,7 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
title,
|
title,
|
||||||
title_regex,
|
title_regex,
|
||||||
),
|
),
|
||||||
(app_id, app_id_regex),
|
(app_id, app_id_regex, floating),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(str("name")),
|
opt(str("name")),
|
||||||
|
|
@ -69,7 +69,11 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
opt(str("title")),
|
opt(str("title")),
|
||||||
opt(str("title-regex")),
|
opt(str("title-regex")),
|
||||||
),
|
),
|
||||||
(opt(str("app-id")), opt(str("app-id-regex"))),
|
(
|
||||||
|
opt(str("app-id")),
|
||||||
|
opt(str("app-id-regex")),
|
||||||
|
opt(bol("floating")),
|
||||||
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut not = None;
|
let mut not = None;
|
||||||
if let Some(value) = not_val {
|
if let Some(value) = not_val {
|
||||||
|
|
@ -114,6 +118,7 @@ impl Parser for WindowMatchParser<'_> {
|
||||||
title_regex: title_regex.despan_into(),
|
title_regex: title_regex.despan_into(),
|
||||||
app_id: app_id.despan_into(),
|
app_id: app_id.despan_into(),
|
||||||
app_id_regex: app_id_regex.despan_into(),
|
app_id_regex: app_id_regex.despan_into(),
|
||||||
|
floating: floating.despan(),
|
||||||
types,
|
types,
|
||||||
client,
|
client,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,6 @@ impl Rule for WindowRule {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[expect(unused_macros)]
|
|
||||||
macro_rules! bool {
|
macro_rules! bool {
|
||||||
($ty:ident, $field:ident) => {
|
($ty:ident, $field:ident) => {
|
||||||
if let Some(value) = &match_.$field {
|
if let Some(value) = &match_.$field {
|
||||||
|
|
@ -259,6 +258,7 @@ impl Rule for WindowRule {
|
||||||
value!(TitleRegex, title_regex);
|
value!(TitleRegex, title_regex);
|
||||||
value!(AppId, app_id);
|
value!(AppId, app_id);
|
||||||
value!(AppIdRegex, app_id_regex);
|
value!(AppIdRegex, app_id_regex);
|
||||||
|
bool!(Floating, floating);
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1795,6 +1795,10 @@
|
||||||
"app-id-regex": {
|
"app-id-regex": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Matches the app-id of the window with a regular expression."
|
"description": "Matches the app-id of the window with a regular expression."
|
||||||
|
},
|
||||||
|
"floating": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Matches if the window is/isn't floating."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
|
||||||
|
|
@ -4034,6 +4034,12 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a string.
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `floating` (optional):
|
||||||
|
|
||||||
|
Matches if the window is/isn't floating.
|
||||||
|
|
||||||
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-WindowMatchExactly"></a>
|
<a name="types-WindowMatchExactly"></a>
|
||||||
### `WindowMatchExactly`
|
### `WindowMatchExactly`
|
||||||
|
|
|
||||||
|
|
@ -3483,6 +3483,10 @@ WindowMatch:
|
||||||
kind: string
|
kind: string
|
||||||
required: false
|
required: false
|
||||||
description: Matches the app-id of the window with a regular expression.
|
description: Matches the app-id of the window with a regular expression.
|
||||||
|
floating:
|
||||||
|
kind: boolean
|
||||||
|
required: false
|
||||||
|
description: Matches if the window is/isn't floating.
|
||||||
|
|
||||||
|
|
||||||
WindowMatchExactly:
|
WindowMatchExactly:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue