Skip to content

Commit

Permalink
Add toggle-window-rule-opacity action
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Jan 23, 2025
1 parent b01b8af commit a10705f
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 2 deletions.
7 changes: 7 additions & 0 deletions niri-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,9 @@ pub enum Action {
x: PositionChange,
y: PositionChange,
},
ToggleWindowRuleOpacity,
#[knuffel(skip)]
ToggleWindowRuleOpacityById(u64),
}

impl From<niri_ipc::Action> for Action {
Expand Down Expand Up @@ -1623,6 +1626,10 @@ impl From<niri_ipc::Action> for Action {
niri_ipc::Action::MoveFloatingWindow { id, x, y } => {
Self::MoveFloatingWindowById { id, x, y }
}
niri_ipc::Action::ToggleWindowRuleOpacity { id: None } => Self::ToggleWindowRuleOpacity,
niri_ipc::Action::ToggleWindowRuleOpacity { id: Some(id) } => {
Self::ToggleWindowRuleOpacityById(id)
}
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,18 @@ pub enum Action {
)]
y: PositionChange,
},
/// Toggle the opacity of a window.
#[cfg_attr(
feature = "clap",
clap(about = "Toggle the opacity of the focused window")
)]
ToggleWindowRuleOpacity {
/// Id of the window.
///
/// If `None`, uses the focused window.
#[cfg_attr(feature = "clap", arg(long))]
id: Option<u64>,
},
}

/// Change in window or column size.
Expand Down
4 changes: 4 additions & 0 deletions niri-visual-tests/src/test_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ impl LayoutElement for TestWindow {

fn set_bounds(&self, _bounds: Size<i32, Logical>) {}

fn is_ignoring_opacity_window_rule(&self) -> bool {
false
}

fn configure_intent(&self) -> ConfigureIntent {
ConfigureIntent::CanSend
}
Expand Down
29 changes: 29 additions & 0 deletions src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use self::move_grab::MoveGrab;
use self::resize_grab::ResizeGrab;
use self::spatial_movement_grab::SpatialMovementGrab;
use crate::layout::scrolling::ScrollDirection;
use crate::layout::LayoutElement as _;
use crate::niri::State;
use crate::ui::screenshot_ui::ScreenshotUi;
use crate::utils::spawning::spawn;
Expand Down Expand Up @@ -1574,6 +1575,34 @@ impl State {
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::ToggleWindowRuleOpacity => {
let active_window = self
.niri
.layout
.active_workspace_mut()
.and_then(|ws| ws.active_window_mut());
if let Some(window) = active_window {
if window.rules().opacity.is_some_and(|o| o != 1.) {
window.toggle_ignore_opacity_window_rule();
// FIXME: granular
self.niri.queue_redraw_all();
}
}
}
Action::ToggleWindowRuleOpacityById(id) => {
let window = self
.niri
.layout
.workspaces_mut()
.find_map(|ws| ws.windows_mut().find(|w| w.id().get() == id));
if let Some(window) = window {
if window.rules().opacity.is_some_and(|o| o != 1.) {
window.toggle_ignore_opacity_window_rule();
// FIXME: granular
self.niri.queue_redraw_all();
}
}
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/layout/floating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,14 @@ impl<W: LayoutElement> FloatingSpace<W> {
.map(Tile::window)
}

pub fn active_window_mut(&mut self) -> Option<&mut W> {
let id = self.active_window_id.as_ref()?;
self.tiles
.iter_mut()
.find(|tile| tile.window().id() == id)
.map(Tile::window_mut)
}

pub fn has_window(&self, id: &W::Id) -> bool {
self.tiles.iter().any(|tile| tile.window().id() == id)
}
Expand Down
5 changes: 5 additions & 0 deletions src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub trait LayoutElement {
fn set_active_in_column(&mut self, active: bool);
fn set_floating(&mut self, floating: bool);
fn set_bounds(&self, bounds: Size<i32, Logical>);
fn is_ignoring_opacity_window_rule(&self) -> bool;

fn configure_intent(&self) -> ConfigureIntent;
fn send_pending_configure(&mut self);
Expand Down Expand Up @@ -4347,6 +4348,10 @@ mod tests {

fn set_bounds(&self, _bounds: Size<i32, Logical>) {}

fn is_ignoring_opacity_window_rule(&self) -> bool {
false
}

fn configure_intent(&self) -> ConfigureIntent {
ConfigureIntent::CanSend
}
Expand Down
9 changes: 9 additions & 0 deletions src/layout/scrolling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,15 @@ impl<W: LayoutElement> ScrollingSpace<W> {
Some(col.tiles[col.active_tile_idx].window())
}

pub fn active_window_mut(&mut self) -> Option<&mut W> {
if self.columns.is_empty() {
return None;
}

let col = &mut self.columns[self.active_column_idx];
Some(col.tiles[col.active_tile_idx].window_mut())
}

pub fn active_tile_mut(&mut self) -> Option<&mut Tile<W>> {
if self.columns.is_empty() {
return None;
Expand Down
2 changes: 1 addition & 1 deletion src/layout/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ impl<W: LayoutElement> Tile<W> {
) -> impl Iterator<Item = TileRenderElement<R>> + 'a {
let _span = tracy_client::span!("Tile::render_inner");

let alpha = if self.is_fullscreen {
let alpha = if self.is_fullscreen || self.window.is_ignoring_opacity_window_rule() {
1.
} else {
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
Expand Down
8 changes: 8 additions & 0 deletions src/layout/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ impl<W: LayoutElement> Workspace<W> {
}
}

pub fn active_window_mut(&mut self) -> Option<&mut W> {
if self.floating_is_active.get() {
self.floating.active_window_mut()
} else {
self.scrolling.active_window_mut()
}
}

pub fn is_active_fullscreen(&self) -> bool {
self.scrolling.is_active_fullscreen()
}
Expand Down
2 changes: 1 addition & 1 deletion src/niri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4597,7 +4597,7 @@ impl Niri {
let _span = tracy_client::span!("Niri::screenshot_window");

let scale = Scale::from(output.current_scale().fractional_scale());
let alpha = if mapped.is_fullscreen() {
let alpha = if mapped.is_fullscreen() || mapped.is_ignoring_opacity_window_rule() {
1.
} else {
mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.)
Expand Down
18 changes: 18 additions & 0 deletions src/window/mapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ pub struct Mapped {
/// Whether this window is floating.
is_floating: bool,

/// Whether this window should ignore opacity set through window rules.
ignore_opacity_window_rule: bool,

/// Buffer to draw instead of the window when it should be blocked out.
block_out_buffer: RefCell<SolidColorBuffer>,

Expand Down Expand Up @@ -167,6 +170,7 @@ impl Mapped {
is_focused: false,
is_active_in_column: true,
is_floating: false,
ignore_opacity_window_rule: false,
block_out_buffer: RefCell::new(SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.])),
animate_next_configure: false,
animate_serials: Vec::new(),
Expand All @@ -192,6 +196,12 @@ impl Mapped {
return false;
}

// If the opacity window rule no longer makes the window semitransparent, reset the ignore
// flag to reduce surprises down the line.
if !new_rules.opacity.is_some_and(|o| o < 1.) {
self.ignore_opacity_window_rule = false;
}

self.rules = new_rules;
true
}
Expand Down Expand Up @@ -228,6 +238,10 @@ impl Mapped {
self.is_floating
}

pub fn toggle_ignore_opacity_window_rule(&mut self) {
self.ignore_opacity_window_rule = !self.ignore_opacity_window_rule;
}

pub fn set_is_focused(&mut self, is_focused: bool) {
if self.is_focused == is_focused {
return;
Expand Down Expand Up @@ -839,6 +853,10 @@ impl LayoutElement for Mapped {
.with_pending_state(|state| state.states.contains(xdg_toplevel::State::Fullscreen))
}

fn is_ignoring_opacity_window_rule(&self) -> bool {
self.ignore_opacity_window_rule
}

fn requested_size(&self) -> Option<Size<i32, Logical>> {
self.toplevel().with_pending_state(|state| state.size)
}
Expand Down
11 changes: 11 additions & 0 deletions wiki/Configuration:-Key-Bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,14 @@ Or, in scripts:
```shell
niri msg action do-screen-transition --delay-ms 100
```

#### `toggle-window-rule-opacity`

Toggle the opacity window rule of the focused window.
This only has an effect if the window's opacity window rule is already set to semitransparent.

```kdl
binds {
Mod+O { toggle-window-rule-opacity; }
}
```
2 changes: 2 additions & 0 deletions wiki/Configuration:-Window-Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ This is applied on top of the window's own opacity, so semitransparent windows w
Opacity is applied to every surface of the window individually, so subsurfaces and pop-up menus will show window content behind them.
Opacity can be toggled on or off for a window using the [`toggle-window-rule-opacity`](./Configuration:-Key-Bindings.md) action.
![Screenshot showing Adwaita Demo with a semitransparent pop-up menu.](./img/opacity-popup.png)
Also, focus ring and border with background will show through semitransparent windows (see `prefer-no-csd` and the `draw-border-with-background` window rule below).
Expand Down

0 comments on commit a10705f

Please sign in to comment.