This commit is contained in:
parent
4c748be113
commit
cb42bf7aee
3 changed files with 127 additions and 43 deletions
|
|
@ -47,10 +47,11 @@ impl NiriTag {
|
|||
ts.occupied = false;
|
||||
}
|
||||
});
|
||||
if let Some(old) = self.tags.get(&old_tag) {
|
||||
if old_tag != 0 && !old.occupied {
|
||||
self.fire_event(TagEvent::TagEmpty(old_tag)).await;
|
||||
}
|
||||
if let Some(old) = self.tags.get(&old_tag)
|
||||
&& old_tag != 0
|
||||
&& !old.occupied
|
||||
{
|
||||
self.fire_event(TagEvent::TagEmpty(old_tag)).await;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -208,14 +209,14 @@ impl NiriTag {
|
|||
affected_windows,
|
||||
)
|
||||
.await;
|
||||
if let Some(focus) = focus {
|
||||
if tag_visible {
|
||||
tell(
|
||||
&mut self.socket,
|
||||
Request::Action(Action::FocusWindow { id: focus }),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if let Some(focus) = focus
|
||||
&& tag_visible
|
||||
{
|
||||
tell(
|
||||
&mut self.socket,
|
||||
Request::Action(Action::FocusWindow { id: focus }),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
TagExclusive(t) => {
|
||||
|
|
@ -293,38 +294,68 @@ impl NiriTag {
|
|||
}
|
||||
Receivable::TagCmd(cmd) => match cmd {
|
||||
TagCmd::AddTagToWin(t) => {
|
||||
let wid = self.get_focused_window().await?.id;
|
||||
self.change_window_tag(wid, Some(t)).await?;
|
||||
let entry = self.tags.entry(t).or_default();
|
||||
if entry.windows.len() == 1 && self.config.activation_on_fill {
|
||||
entry.enabled = true;
|
||||
self.fire_event(TagEvent::TagEnabled(t)).await;
|
||||
&[Tag(t), Window(wid)]
|
||||
let win = self.get_focused_window().await?;
|
||||
if win
|
||||
.app_id
|
||||
.as_ref()
|
||||
.is_some_and(|name| self.config.scratchpads.contains_key(name))
|
||||
{
|
||||
&[]
|
||||
} else {
|
||||
&[Window(wid)]
|
||||
let wid = win.id;
|
||||
self.change_window_tag(wid, Some(t)).await?;
|
||||
let entry = self.tags.entry(t).or_default();
|
||||
if entry.windows.len() == 1 && self.config.activation_on_fill {
|
||||
entry.enabled = true;
|
||||
self.fire_event(TagEvent::TagEnabled(t)).await;
|
||||
&[Tag(t), Window(wid)]
|
||||
} else {
|
||||
&[Window(wid)]
|
||||
}
|
||||
}
|
||||
}
|
||||
TagCmd::RemoveTagFromWin(_) => {
|
||||
let wid = self.get_focused_window().await?.id;
|
||||
self.change_window_tag(wid, None).await?;
|
||||
&[Window(wid)]
|
||||
let win = self.get_focused_window().await?;
|
||||
if win
|
||||
.app_id
|
||||
.as_ref()
|
||||
.is_some_and(|name| self.config.scratchpads.contains_key(name))
|
||||
{
|
||||
&[]
|
||||
} else {
|
||||
let wid = win.id;
|
||||
self.change_window_tag(wid, None).await?;
|
||||
&[Window(wid)]
|
||||
}
|
||||
}
|
||||
TagCmd::ToggleTagOnWin(t) => {
|
||||
let wid = self.get_focused_window().await?.id;
|
||||
let new_tag = if *self.windows.entry(wid).or_insert(0) == t {
|
||||
0
|
||||
let win = self.get_focused_window().await?;
|
||||
if win
|
||||
.app_id
|
||||
.as_ref()
|
||||
.is_some_and(|name| self.config.scratchpads.contains_key(name))
|
||||
{
|
||||
&[]
|
||||
} else {
|
||||
t
|
||||
};
|
||||
self.change_window_tag(wid, Some(new_tag)).await?;
|
||||
tracing::debug!("toggling {} to tag {}", wid, new_tag);
|
||||
let entry = self.tags.entry(new_tag).or_default();
|
||||
if new_tag != 0 && entry.windows.len() == 1 && self.config.activation_on_fill {
|
||||
entry.enabled = true;
|
||||
self.fire_event(TagEvent::TagEnabled(t)).await;
|
||||
&[Tag(t), Window(wid)]
|
||||
} else {
|
||||
&[Window(wid)]
|
||||
let wid = win.id;
|
||||
let new_tag = if *self.windows.entry(wid).or_insert(0) == t {
|
||||
0
|
||||
} else {
|
||||
t
|
||||
};
|
||||
self.change_window_tag(wid, Some(new_tag)).await?;
|
||||
tracing::debug!("toggling {} to tag {}", wid, new_tag);
|
||||
let entry = self.tags.entry(new_tag).or_default();
|
||||
if new_tag != 0
|
||||
&& entry.windows.len() == 1
|
||||
&& self.config.activation_on_fill
|
||||
{
|
||||
entry.enabled = true;
|
||||
self.fire_event(TagEvent::TagEnabled(t)).await;
|
||||
&[Tag(t), Window(wid)]
|
||||
} else {
|
||||
&[Window(wid)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +417,26 @@ impl NiriTag {
|
|||
use Event::*;
|
||||
match ev {
|
||||
WindowOpenedOrChanged { window } => {
|
||||
self.windows.entry(window.id).or_insert(0);
|
||||
let wid = window.id;
|
||||
let current_tag = self.windows.get(&wid).copied();
|
||||
// only reassign if window is new or still untagged
|
||||
// this handles the common case where app_id: None arrives first
|
||||
if current_tag.is_none() || current_tag == Some(0) {
|
||||
if let Some(ref app_id) = window.app_id
|
||||
&& let Some(&tag) = self.config.scratchpads.get(app_id)
|
||||
{
|
||||
tracing::debug!(
|
||||
"scratchpad: auto-assigning wid={} app_id={} to tag {}",
|
||||
wid,
|
||||
app_id,
|
||||
tag
|
||||
);
|
||||
self.change_window_tag(wid, Some(tag)).await?;
|
||||
self.do_actions(&[TagAction::Window(wid)]).await?;
|
||||
} else if current_tag.is_none() {
|
||||
self.windows.insert(wid, 0);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
WindowClosed { id } => {
|
||||
|
|
@ -395,10 +445,10 @@ impl NiriTag {
|
|||
ts.windows.remove(&id);
|
||||
ts.occupied = !ts.windows.is_empty();
|
||||
});
|
||||
if let Some(tag) = self.tags.get(&t) {
|
||||
if !tag.occupied {
|
||||
self.fire_event(TagEvent::TagEmpty(t)).await;
|
||||
}
|
||||
if let Some(tag) = self.tags.get(&t)
|
||||
&& !tag.occupied
|
||||
{
|
||||
self.fire_event(TagEvent::TagEmpty(t)).await;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -477,7 +527,6 @@ impl NiriTag {
|
|||
(0..=self.config.prepopulate).for_each(|i| {
|
||||
self.tags.entry(i).or_default();
|
||||
});
|
||||
|
||||
loop {
|
||||
let recvd: Receivable = future::or(
|
||||
async { ev_rx.recv().await.map(Receivable::Event) },
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ pub struct Config {
|
|||
pub strict: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub activation_on_fill: bool,
|
||||
pub scratchpads: HashMap<String, u8>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
|
@ -68,6 +69,7 @@ impl Default for Config {
|
|||
prepopulate: 3,
|
||||
strict: true,
|
||||
activation_on_fill: true,
|
||||
scratchpads: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
module.nix
33
module.nix
|
|
@ -8,9 +8,12 @@ let
|
|||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkPackageOption
|
||||
mkOption
|
||||
mkIf
|
||||
getExe
|
||||
types
|
||||
;
|
||||
inherit (types) attrsOf bool;
|
||||
name = "Niri Tag Manager";
|
||||
in
|
||||
{
|
||||
|
|
@ -20,6 +23,18 @@ in
|
|||
nullable = true;
|
||||
default = "niri-tag";
|
||||
};
|
||||
prepopulate = mkOption {
|
||||
type = types.numbers.between 0 255;
|
||||
default = 3;
|
||||
};
|
||||
scratchpads = mkOption {
|
||||
type = attrsOf (types.numbers.between 1 255);
|
||||
default = { };
|
||||
};
|
||||
strict = mkOption {
|
||||
type = bool;
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
config =
|
||||
let
|
||||
|
|
@ -40,6 +55,24 @@ in
|
|||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
environment.etc."niri-tag/config.toml".source =
|
||||
let
|
||||
scratchpads =
|
||||
let
|
||||
contents =
|
||||
lib.mapAttrsToList (app: number: "${app} = ${toString number}\n") cfg.scratchpads
|
||||
|> (lib.flip lib.concatStringsSep "\n");
|
||||
in
|
||||
lib.optionalString ((builtins.length <| lib.attrsToList cfg.scratchpads) > 0) ''
|
||||
[scratchpads]
|
||||
${contents}
|
||||
'';
|
||||
in
|
||||
''
|
||||
prepopulate = ${toString cfg.prepopulate}
|
||||
strict = ${lib.boolToString cfg.strict}
|
||||
${scratchpads}
|
||||
'';
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue