seat: add framework to select toplevels
This commit is contained in:
parent
e4e090d3a2
commit
17a0dfed5e
31 changed files with 603 additions and 131 deletions
|
|
@ -257,6 +257,10 @@ pub mod colors {
|
||||||
///
|
///
|
||||||
/// Default: `#23092c`.
|
/// Default: `#23092c`.
|
||||||
const 14 => ATTENTION_REQUESTED_BACKGROUND_COLOR,
|
const 14 => ATTENTION_REQUESTED_BACKGROUND_COLOR,
|
||||||
|
/// Color used to highlight parts of the UI.
|
||||||
|
///
|
||||||
|
/// Default: `#9d28c67f`.
|
||||||
|
const 15 => HIGHLIGHT_COLOR,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the color of GUI element.
|
/// Sets the color of GUI element.
|
||||||
|
|
|
||||||
|
|
@ -1430,6 +1430,7 @@ impl ConfigProxyHandler {
|
||||||
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => &colors.focused_inactive_title_text,
|
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => &colors.focused_inactive_title_text,
|
||||||
BAR_STATUS_TEXT_COLOR => &colors.bar_text,
|
BAR_STATUS_TEXT_COLOR => &colors.bar_text,
|
||||||
ATTENTION_REQUESTED_BACKGROUND_COLOR => &colors.attention_requested_background,
|
ATTENTION_REQUESTED_BACKGROUND_COLOR => &colors.attention_requested_background,
|
||||||
|
HIGHLIGHT_COLOR => &colors.highlight,
|
||||||
_ => return Err(CphError::UnknownColor(colorable.0)),
|
_ => return Err(CphError::UnknownColor(colorable.0)),
|
||||||
};
|
};
|
||||||
Ok(colorable)
|
Ok(colorable)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ pub mod zwp_relative_pointer_v1;
|
||||||
pub mod zwp_virtual_keyboard_manager_v1;
|
pub mod zwp_virtual_keyboard_manager_v1;
|
||||||
pub mod zwp_virtual_keyboard_v1;
|
pub mod zwp_virtual_keyboard_v1;
|
||||||
|
|
||||||
pub use event_handling::NodeSeatState;
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
|
|
@ -84,6 +83,7 @@ use {
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::OwnedFd,
|
uapi::OwnedFd,
|
||||||
};
|
};
|
||||||
|
pub use {event_handling::NodeSeatState, pointer_owner::ToplevelSelector};
|
||||||
|
|
||||||
pub const POINTER: u32 = 1;
|
pub const POINTER: u32 = 1;
|
||||||
const KEYBOARD: u32 = 2;
|
const KEYBOARD: u32 = 2;
|
||||||
|
|
@ -94,6 +94,7 @@ const TOUCH: u32 = 4;
|
||||||
const MISSING_CAPABILITY: u32 = 0;
|
const MISSING_CAPABILITY: u32 = 0;
|
||||||
|
|
||||||
pub const BTN_LEFT: u32 = 0x110;
|
pub const BTN_LEFT: u32 = 0x110;
|
||||||
|
pub const BTN_RIGHT: u32 = 0x111;
|
||||||
|
|
||||||
pub const SEAT_NAME_SINCE: Version = Version(2);
|
pub const SEAT_NAME_SINCE: Version = Version(2);
|
||||||
|
|
||||||
|
|
@ -1151,6 +1152,11 @@ impl WlSeatGlobal {
|
||||||
pub fn set_forward(&self, forward: bool) {
|
pub fn set_forward(&self, forward: bool) {
|
||||||
self.forward.set(forward);
|
self.forward.set(forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn select_toplevel(self: &Rc<Self>, selector: impl ToplevelSelector) {
|
||||||
|
self.pointer_owner.select_toplevel(self, selector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,46 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{AxisSource, KeyState, ScrollAxis, AXIS_120},
|
backend::{AxisSource, KeyState, ScrollAxis, AXIS_120},
|
||||||
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
ifs::{
|
ifs::{
|
||||||
ipc,
|
ipc,
|
||||||
ipc::wl_data_source::WlDataSource,
|
ipc::wl_data_source::WlDataSource,
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal,
|
wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal, BTN_LEFT,
|
||||||
CHANGE_CURSOR_MOVED,
|
BTN_RIGHT, CHANGE_CURSOR_MOVED,
|
||||||
},
|
},
|
||||||
wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||||
},
|
},
|
||||||
state::DeviceHandlerData,
|
state::DeviceHandlerData,
|
||||||
tree::{FoundNode, Node},
|
tree::{FindTreeUsecase, FoundNode, Node, ToplevelNode},
|
||||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{
|
||||||
|
cell::Cell,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PointerOwnerHolder {
|
pub struct PointerOwnerHolder {
|
||||||
default: Rc<DefaultPointerOwner>,
|
default: Rc<SimplePointerOwner<DefaultPointerUsecase>>,
|
||||||
owner: CloneCell<Rc<dyn PointerOwner>>,
|
owner: CloneCell<Rc<dyn PointerOwner>>,
|
||||||
pending_scroll: PendingScroll,
|
pending_scroll: PendingScroll,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ToplevelSelector: 'static {
|
||||||
|
fn set(&self, toplevel: Rc<dyn ToplevelNode>);
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PointerOwnerHolder {
|
impl Default for PointerOwnerHolder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let default = Rc::new(SimplePointerOwner {
|
||||||
|
usecase: DefaultPointerUsecase,
|
||||||
|
});
|
||||||
Self {
|
Self {
|
||||||
default: Rc::new(DefaultPointerOwner),
|
default: default.clone(),
|
||||||
owner: CloneCell::new(Rc::new(DefaultPointerOwner)),
|
owner: CloneCell::new(default.clone()),
|
||||||
pending_scroll: Default::default(),
|
pending_scroll: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,6 +156,20 @@ impl PointerOwnerHolder {
|
||||||
seat.pointer_owner.owner.set(self.default.clone());
|
seat.pointer_owner.owner.set(self.default.clone());
|
||||||
seat.changes.or_assign(CHANGE_CURSOR_MOVED);
|
seat.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_toplevel(&self, seat: &Rc<WlSeatGlobal>, selector: impl ToplevelSelector) {
|
||||||
|
self.revert_to_default(seat);
|
||||||
|
let usecase = Rc::new(SelectToplevelUsecase {
|
||||||
|
seat: Rc::downgrade(seat),
|
||||||
|
selector,
|
||||||
|
latest: Default::default(),
|
||||||
|
});
|
||||||
|
if let Some(node) = seat.pointer_stack.borrow().last() {
|
||||||
|
usecase.node_focus(seat, node);
|
||||||
|
}
|
||||||
|
self.owner.set(Rc::new(SimplePointerOwner { usecase }));
|
||||||
|
seat.trigger_tree_changed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PointerOwner {
|
trait PointerOwner {
|
||||||
|
|
@ -167,9 +192,12 @@ trait PointerOwner {
|
||||||
fn remove_dnd_icon(&self);
|
fn remove_dnd_icon(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DefaultPointerOwner;
|
struct SimplePointerOwner<T> {
|
||||||
|
usecase: T,
|
||||||
|
}
|
||||||
|
|
||||||
struct GrabPointerOwner {
|
struct SimpleGrabPointerOwner<T> {
|
||||||
|
usecase: T,
|
||||||
buttons: SmallMap<u32, (), 1>,
|
buttons: SmallMap<u32, (), 1>,
|
||||||
node: Rc<dyn Node>,
|
node: Rc<dyn Node>,
|
||||||
serial: u32,
|
serial: u32,
|
||||||
|
|
@ -184,7 +212,16 @@ struct DndPointerOwner {
|
||||||
pos_y: Cell<Fixed>,
|
pos_y: Cell<Fixed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerOwner for DefaultPointerOwner {
|
#[derive(Copy, Clone)]
|
||||||
|
struct DefaultPointerUsecase;
|
||||||
|
|
||||||
|
struct SelectToplevelUsecase<S: ?Sized> {
|
||||||
|
seat: Weak<WlSeatGlobal>,
|
||||||
|
latest: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
||||||
|
selector: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
|
||||||
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
||||||
if state != KeyState::Pressed {
|
if state != KeyState::Pressed {
|
||||||
return;
|
return;
|
||||||
|
|
@ -193,12 +230,18 @@ impl PointerOwner for DefaultPointerOwner {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
if self.usecase.default_button(self, seat, button, &pn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let serial = seat.state.next_serial(pn.node_client().as_deref());
|
let serial = seat.state.next_serial(pn.node_client().as_deref());
|
||||||
seat.pointer_owner.owner.set(Rc::new(GrabPointerOwner {
|
seat.pointer_owner
|
||||||
buttons: SmallMap::new_with(button, ()),
|
.owner
|
||||||
node: pn.clone(),
|
.set(Rc::new(SimpleGrabPointerOwner {
|
||||||
serial,
|
usecase: self.usecase.clone(),
|
||||||
}));
|
buttons: SmallMap::new_with(button, ()),
|
||||||
|
node: pn.clone(),
|
||||||
|
serial,
|
||||||
|
}));
|
||||||
pn.node_seat_state().add_pointer_grab(seat);
|
pn.node_seat_state().add_pointer_grab(seat);
|
||||||
pn.node_on_button(seat, time_usec, button, state, serial);
|
pn.node_on_button(seat, time_usec, button, state, serial);
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +263,7 @@ impl PointerOwner for DefaultPointerOwner {
|
||||||
});
|
});
|
||||||
seat.state
|
seat.state
|
||||||
.root
|
.root
|
||||||
.node_find_tree_at(x_int, y_int, &mut found_tree);
|
.node_find_tree_at(x_int, y_int, &mut found_tree, T::FIND_TREE_USECASE);
|
||||||
let mut divergence = found_tree.len().min(stack.len());
|
let mut divergence = found_tree.len().min(stack.len());
|
||||||
for (i, (found, stack)) in found_tree.iter().zip(stack.iter()).enumerate() {
|
for (i, (found, stack)) in found_tree.iter().zip(stack.iter()).enumerate() {
|
||||||
if found.node.node_id() != stack.node_id() {
|
if found.node.node_id() != stack.node_id() {
|
||||||
|
|
@ -266,6 +309,7 @@ impl PointerOwner for DefaultPointerOwner {
|
||||||
}
|
}
|
||||||
if let Some(node) = stack.last() {
|
if let Some(node) = stack.last() {
|
||||||
node.node_on_pointer_focus(seat);
|
node.node_on_pointer_focus(seat);
|
||||||
|
self.usecase.node_focus(seat, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
found_tree.clear();
|
found_tree.clear();
|
||||||
|
|
@ -289,8 +333,12 @@ impl PointerOwner for DefaultPointerOwner {
|
||||||
seat.dropped_dnd.borrow_mut().take();
|
seat.dropped_dnd.borrow_mut().take();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn revert_to_default(&self, _seat: &Rc<WlSeatGlobal>) {
|
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
// nothing
|
if !T::IS_DEFAULT {
|
||||||
|
seat.pointer_owner.set_default_pointer_owner(seat);
|
||||||
|
seat.trigger_tree_changed();
|
||||||
|
seat.state.damage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dnd_target_removed(&self, seat: &Rc<WlSeatGlobal>) {
|
fn dnd_target_removed(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
|
@ -310,7 +358,7 @@ impl PointerOwner for DefaultPointerOwner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerOwner for GrabPointerOwner {
|
impl<T: SimplePointerOwnerUsecase> PointerOwner for SimpleGrabPointerOwner<T> {
|
||||||
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
||||||
match state {
|
match state {
|
||||||
KeyState::Released => {
|
KeyState::Released => {
|
||||||
|
|
@ -318,7 +366,7 @@ impl PointerOwner for GrabPointerOwner {
|
||||||
if self.buttons.is_empty() {
|
if self.buttons.is_empty() {
|
||||||
self.node.node_seat_state().remove_pointer_grab(seat);
|
self.node.node_seat_state().remove_pointer_grab(seat);
|
||||||
// log::info!("button");
|
// log::info!("button");
|
||||||
seat.pointer_owner.set_default_pointer_owner(seat);
|
self.usecase.release_grab(seat);
|
||||||
seat.tree_changed.trigger();
|
seat.tree_changed.trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -354,57 +402,8 @@ impl PointerOwner for GrabPointerOwner {
|
||||||
icon: Option<Rc<WlSurface>>,
|
icon: Option<Rc<WlSurface>>,
|
||||||
serial: u32,
|
serial: u32,
|
||||||
) -> Result<(), WlSeatError> {
|
) -> Result<(), WlSeatError> {
|
||||||
let button = match self.buttons.iter().next() {
|
self.usecase
|
||||||
Some((b, _)) => b,
|
.start_drag(self, seat, origin, src, icon, serial)
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
if self.buttons.len() != 1 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if serial != self.serial {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if self.node.node_id() != origin.node_id {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if let Some(icon) = &icon {
|
|
||||||
icon.set_dnd_icon_seat(seat.id, Some(seat));
|
|
||||||
}
|
|
||||||
if let Some(new) = &src {
|
|
||||||
ipc::attach_seat(&**new, seat, ipc::Role::Dnd)?;
|
|
||||||
if let Some(drag) = new.toplevel_drag.get() {
|
|
||||||
drag.start_drag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*seat.dropped_dnd.borrow_mut() = None;
|
|
||||||
let pointer_owner = Rc::new(DndPointerOwner {
|
|
||||||
button,
|
|
||||||
dnd: Dnd {
|
|
||||||
seat: seat.clone(),
|
|
||||||
client: origin.client.clone(),
|
|
||||||
src,
|
|
||||||
},
|
|
||||||
target: CloneCell::new(seat.state.root.clone()),
|
|
||||||
icon: CloneCell::new(icon),
|
|
||||||
pos_x: Cell::new(Fixed::from_int(0)),
|
|
||||||
pos_y: Cell::new(Fixed::from_int(0)),
|
|
||||||
});
|
|
||||||
{
|
|
||||||
let mut stack = seat.pointer_stack.borrow_mut();
|
|
||||||
for node in stack.drain(1..).rev() {
|
|
||||||
node.node_on_leave(seat);
|
|
||||||
node.node_seat_state().leave(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.node.node_seat_state().remove_pointer_grab(seat);
|
|
||||||
// {
|
|
||||||
// let old = seat.keyboard_node.set(seat.state.root.clone());
|
|
||||||
// old.seat_state().unfocus(seat);
|
|
||||||
// old.unfocus(seat);
|
|
||||||
// }
|
|
||||||
seat.pointer_owner.owner.set(pointer_owner.clone());
|
|
||||||
pointer_owner.apply_changes(seat);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_dnd(&self, seat: &Rc<WlSeatGlobal>) {
|
fn cancel_dnd(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
|
@ -482,7 +481,7 @@ impl PointerOwner for DndPointerOwner {
|
||||||
});
|
});
|
||||||
seat.state
|
seat.state
|
||||||
.root
|
.root
|
||||||
.node_find_tree_at(x_int, y_int, &mut found_tree);
|
.node_find_tree_at(x_int, y_int, &mut found_tree, FindTreeUsecase::None);
|
||||||
let FoundNode { node, x, y } = found_tree.pop().unwrap();
|
let FoundNode { node, x, y } = found_tree.pop().unwrap();
|
||||||
found_tree.clear();
|
found_tree.clear();
|
||||||
(node, x, y)
|
(node, x, y)
|
||||||
|
|
@ -562,3 +561,192 @@ impl PointerOwner for DndPointerOwner {
|
||||||
self.icon.set(None);
|
self.icon.set(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait SimplePointerOwnerUsecase: Sized + Clone + 'static {
|
||||||
|
const FIND_TREE_USECASE: FindTreeUsecase;
|
||||||
|
const IS_DEFAULT: bool;
|
||||||
|
|
||||||
|
fn default_button(
|
||||||
|
&self,
|
||||||
|
spo: &SimplePointerOwner<Self>,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
button: u32,
|
||||||
|
pn: &Rc<dyn Node>,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
|
fn start_drag(
|
||||||
|
&self,
|
||||||
|
grab: &SimpleGrabPointerOwner<Self>,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
origin: &Rc<WlSurface>,
|
||||||
|
src: Option<Rc<WlDataSource>>,
|
||||||
|
icon: Option<Rc<WlSurface>>,
|
||||||
|
serial: u32,
|
||||||
|
) -> Result<(), WlSeatError>;
|
||||||
|
|
||||||
|
fn release_grab(&self, seat: &Rc<WlSeatGlobal>);
|
||||||
|
|
||||||
|
fn node_focus(&self, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
|
||||||
|
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::None;
|
||||||
|
const IS_DEFAULT: bool = true;
|
||||||
|
|
||||||
|
fn default_button(
|
||||||
|
&self,
|
||||||
|
_spo: &SimplePointerOwner<Self>,
|
||||||
|
_seat: &Rc<WlSeatGlobal>,
|
||||||
|
_button: u32,
|
||||||
|
_pn: &Rc<dyn Node>,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_drag(
|
||||||
|
&self,
|
||||||
|
grab: &SimpleGrabPointerOwner<Self>,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
origin: &Rc<WlSurface>,
|
||||||
|
src: Option<Rc<WlDataSource>>,
|
||||||
|
icon: Option<Rc<WlSurface>>,
|
||||||
|
serial: u32,
|
||||||
|
) -> Result<(), WlSeatError> {
|
||||||
|
let button = match grab.buttons.iter().next() {
|
||||||
|
Some((b, _)) => b,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
if grab.buttons.len() != 1 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if serial != grab.serial {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if grab.node.node_id() != origin.node_id {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(icon) = &icon {
|
||||||
|
icon.set_dnd_icon_seat(seat.id, Some(seat));
|
||||||
|
}
|
||||||
|
if let Some(new) = &src {
|
||||||
|
ipc::attach_seat(&**new, seat, ipc::Role::Dnd)?;
|
||||||
|
if let Some(drag) = new.toplevel_drag.get() {
|
||||||
|
drag.start_drag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*seat.dropped_dnd.borrow_mut() = None;
|
||||||
|
let pointer_owner = Rc::new(DndPointerOwner {
|
||||||
|
button,
|
||||||
|
dnd: Dnd {
|
||||||
|
seat: seat.clone(),
|
||||||
|
client: origin.client.clone(),
|
||||||
|
src,
|
||||||
|
},
|
||||||
|
target: CloneCell::new(seat.state.root.clone()),
|
||||||
|
icon: CloneCell::new(icon),
|
||||||
|
pos_x: Cell::new(Fixed::from_int(0)),
|
||||||
|
pos_y: Cell::new(Fixed::from_int(0)),
|
||||||
|
});
|
||||||
|
{
|
||||||
|
let mut stack = seat.pointer_stack.borrow_mut();
|
||||||
|
for node in stack.drain(1..).rev() {
|
||||||
|
node.node_on_leave(seat);
|
||||||
|
node.node_seat_state().leave(seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grab.node.node_seat_state().remove_pointer_grab(seat);
|
||||||
|
// {
|
||||||
|
// let old = seat.keyboard_node.set(seat.state.root.clone());
|
||||||
|
// old.seat_state().unfocus(seat);
|
||||||
|
// old.unfocus(seat);
|
||||||
|
// }
|
||||||
|
seat.pointer_owner.owner.set(pointer_owner.clone());
|
||||||
|
pointer_owner.apply_changes(seat);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release_grab(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
seat.pointer_owner.set_default_pointer_owner(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_focus(&self, _seat: &Rc<WlSeatGlobal>, _node: &Rc<dyn Node>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplevelUsecase<S>> {
|
||||||
|
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::SelectToplevel;
|
||||||
|
const IS_DEFAULT: bool = false;
|
||||||
|
|
||||||
|
fn default_button(
|
||||||
|
&self,
|
||||||
|
spo: &SimplePointerOwner<Self>,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
button: u32,
|
||||||
|
pn: &Rc<dyn Node>,
|
||||||
|
) -> bool {
|
||||||
|
let Some(tl) = pn.clone().node_into_toplevel() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let selected_toplevel =
|
||||||
|
button == BTN_RIGHT || (button == BTN_LEFT && !tl.tl_admits_children());
|
||||||
|
if !selected_toplevel {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.selector.set(tl);
|
||||||
|
spo.revert_to_default(seat);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_drag(
|
||||||
|
&self,
|
||||||
|
_grab: &SimpleGrabPointerOwner<Self>,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
_origin: &Rc<WlSurface>,
|
||||||
|
src: Option<Rc<WlDataSource>>,
|
||||||
|
_icon: Option<Rc<WlSurface>>,
|
||||||
|
_serial: u32,
|
||||||
|
) -> Result<(), WlSeatError> {
|
||||||
|
if let Some(src) = src {
|
||||||
|
src.send_cancelled(seat);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release_grab(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
seat.pointer_owner.owner.set(Rc::new(SimplePointerOwner {
|
||||||
|
usecase: self.clone(),
|
||||||
|
}));
|
||||||
|
seat.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_focus(&self, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
|
||||||
|
let mut damage = false;
|
||||||
|
let tl = node.clone().node_into_toplevel();
|
||||||
|
if let Some(tl) = &tl {
|
||||||
|
tl.tl_data().render_highlight.fetch_add(1);
|
||||||
|
if !tl.tl_admits_children() {
|
||||||
|
seat.set_known_cursor(KnownCursor::Pointer);
|
||||||
|
}
|
||||||
|
damage = true;
|
||||||
|
}
|
||||||
|
if let Some(prev) = self.latest.set(tl) {
|
||||||
|
prev.tl_data().render_highlight.fetch_sub(1);
|
||||||
|
damage = true;
|
||||||
|
}
|
||||||
|
if damage {
|
||||||
|
seat.state.damage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: ?Sized> Drop for SelectToplevelUsecase<S> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(prev) = self.latest.take() {
|
||||||
|
prev.tl_data().render_highlight.fetch_sub(1);
|
||||||
|
if let Some(seat) = self.seat.upgrade() {
|
||||||
|
seat.state.damage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use {
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
|
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
|
||||||
utils::numcell::NumCell,
|
utils::numcell::NumCell,
|
||||||
wire::{ext_session_lock_surface_v1::*, ExtSessionLockSurfaceV1Id, WlSurfaceId},
|
wire::{ext_session_lock_surface_v1::*, ExtSessionLockSurfaceV1Id, WlSurfaceId},
|
||||||
},
|
},
|
||||||
|
|
@ -122,7 +122,13 @@ impl Node for ExtSessionLockSurfaceV1 {
|
||||||
self.surface.node_absolute_position()
|
self.surface.node_absolute_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
_usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
self.surface.find_tree_at_(x, y, tree)
|
self.surface.find_tree_at_(x, y, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
|
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor,
|
||||||
ToplevelData, ToplevelNode, ToplevelNodeBase, WorkspaceNode,
|
StackedNode, ToplevelData, ToplevelNode, ToplevelNodeBase, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
||||||
wire::WlSurfaceId,
|
wire::WlSurfaceId,
|
||||||
|
|
@ -326,7 +326,16 @@ impl Node for Xwindow {
|
||||||
self.toplevel_data.update_self_active(self, active);
|
self.toplevel_data.update_self_active(self, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
|
if usecase == FindTreeUsecase::SelectToplevel {
|
||||||
|
return FindTreeResult::AcceptsInput;
|
||||||
|
}
|
||||||
let rect = self.x.surface.buffer_abs_pos.get();
|
let rect = self.x.surface.buffer_abs_pos.get();
|
||||||
if x < rect.width() && y < rect.height() {
|
if x < rect.width() && y < rect.height() {
|
||||||
tree.push(FoundNode {
|
tree.push(FoundNode {
|
||||||
|
|
@ -340,7 +349,7 @@ impl Node for Xwindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) {
|
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
renderer.render_surface(&self.x.surface, x, y, bounds)
|
renderer.render_xwindow(self, x, y, bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_client(&self) -> Option<Rc<Client>> {
|
fn node_client(&self) -> Option<Rc<Client>> {
|
||||||
|
|
@ -359,6 +368,10 @@ impl Node for Xwindow {
|
||||||
// log::info!("wl-surface focus");
|
// log::info!("wl-surface focus");
|
||||||
seat.set_known_cursor(KnownCursor::Default);
|
seat.set_known_cursor(KnownCursor::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelNodeBase for Xwindow {
|
impl ToplevelNodeBase for Xwindow {
|
||||||
|
|
@ -428,6 +441,10 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||||
Some(self.x.surface.clone())
|
Some(self.x.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_admits_children(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackedNode for Xwindow {
|
impl StackedNode for Xwindow {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@ use {
|
||||||
object::Object,
|
object::Object,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode, WorkspaceNode},
|
tree::{
|
||||||
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
|
||||||
|
WorkspaceNode,
|
||||||
|
},
|
||||||
utils::{clonecell::CloneCell, linkedlist::LinkedNode},
|
utils::{clonecell::CloneCell, linkedlist::LinkedNode},
|
||||||
wire::{xdg_popup::*, XdgPopupId},
|
wire::{xdg_popup::*, XdgPopupId},
|
||||||
},
|
},
|
||||||
|
|
@ -314,7 +317,16 @@ impl Node for XdgPopup {
|
||||||
self.xdg.absolute_desired_extents.get()
|
self.xdg.absolute_desired_extents.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
|
if usecase == FindTreeUsecase::SelectToplevel {
|
||||||
|
return FindTreeResult::Other;
|
||||||
|
}
|
||||||
self.xdg.find_tree_at(x, y, tree)
|
self.xdg.find_tree_at(x, y, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
|
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor,
|
||||||
ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelNodeId, WorkspaceNode,
|
OutputNode, ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelNodeId,
|
||||||
|
WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::clonecell::CloneCell,
|
utils::clonecell::CloneCell,
|
||||||
wire::{xdg_toplevel::*, XdgToplevelId},
|
wire::{xdg_toplevel::*, XdgToplevelId},
|
||||||
|
|
@ -492,12 +493,21 @@ impl Node for XdgToplevel {
|
||||||
self.toplevel_data.update_self_active(self, active);
|
self.toplevel_data.update_self_active(self, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
|
if usecase == FindTreeUsecase::SelectToplevel {
|
||||||
|
return FindTreeResult::AcceptsInput;
|
||||||
|
}
|
||||||
self.xdg.find_tree_at(x, y, tree)
|
self.xdg.find_tree_at(x, y, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) {
|
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
renderer.render_xdg_surface(&self.xdg, x, y, bounds)
|
renderer.render_xdg_toplevel(self, x, y, bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_client(&self) -> Option<Rc<Client>> {
|
fn node_client(&self) -> Option<Rc<Client>> {
|
||||||
|
|
@ -516,6 +526,10 @@ impl Node for XdgToplevel {
|
||||||
// log::info!("xdg-toplevel focus");
|
// log::info!("xdg-toplevel focus");
|
||||||
seat.set_known_cursor(KnownCursor::Default);
|
seat.set_known_cursor(KnownCursor::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelNodeBase for XdgToplevel {
|
impl ToplevelNodeBase for XdgToplevel {
|
||||||
|
|
@ -619,6 +633,10 @@ impl ToplevelNodeBase for XdgToplevel {
|
||||||
fn tl_restack_popups(&self) {
|
fn tl_restack_popups(&self) {
|
||||||
self.xdg.restack_popups();
|
self.xdg.restack_popups();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_admits_children(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XdgSurfaceExt for XdgToplevel {
|
impl XdgSurfaceExt for XdgToplevel {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
object::Object,
|
object::Object,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
|
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
|
||||||
utils::{
|
utils::{
|
||||||
bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell,
|
bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell,
|
||||||
option_ext::OptionExt,
|
option_ext::OptionExt,
|
||||||
|
|
@ -424,7 +424,13 @@ impl Node for ZwlrLayerSurfaceV1 {
|
||||||
self.pos.get()
|
self.pos.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
_usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
self.surface.find_tree_at_(x, y, tree)
|
self.surface.find_tree_at_(x, y, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ mod t0038_subsurface_parent_state;
|
||||||
mod t0039_alpha_modifier;
|
mod t0039_alpha_modifier;
|
||||||
mod t0040_virtual_keyboard;
|
mod t0040_virtual_keyboard;
|
||||||
mod t0041_input_method;
|
mod t0041_input_method;
|
||||||
|
mod t0042_toplevel_select;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -133,5 +134,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0039_alpha_modifier,
|
t0039_alpha_modifier,
|
||||||
t0040_virtual_keyboard,
|
t0040_virtual_keyboard,
|
||||||
t0041_input_method,
|
t0041_input_method,
|
||||||
|
t0042_toplevel_select,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
src/it/tests/t0042_toplevel_select.rs
Normal file
68
src/it/tests/t0042_toplevel_select.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
ifs::wl_seat::{ToplevelSelector, BTN_LEFT},
|
||||||
|
it::{test_error::TestResult, testrun::TestRun},
|
||||||
|
tree::{Node, ToplevelNode},
|
||||||
|
utils::clonecell::CloneCell,
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
testcase!();
|
||||||
|
|
||||||
|
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
let ds = run.create_default_setup().await?;
|
||||||
|
|
||||||
|
let client = run.create_client().await?;
|
||||||
|
let win1 = client.create_window().await?;
|
||||||
|
win1.map2().await?;
|
||||||
|
let win2 = client.create_window().await?;
|
||||||
|
win2.map2().await?;
|
||||||
|
client.sync().await;
|
||||||
|
|
||||||
|
let win1pos = win1.tl.server.node_absolute_position().position();
|
||||||
|
let win2pos = win2.tl.server.node_absolute_position().position();
|
||||||
|
ds.mouse.abs(
|
||||||
|
&ds.connector,
|
||||||
|
win1pos.0 as f64 + 2.0,
|
||||||
|
win1pos.1 as f64 + 2.0,
|
||||||
|
);
|
||||||
|
run.sync().await;
|
||||||
|
|
||||||
|
struct Selector(CloneCell<Option<Rc<dyn ToplevelNode>>>);
|
||||||
|
impl ToplevelSelector for Rc<Selector> {
|
||||||
|
fn set(&self, toplevel: Rc<dyn ToplevelNode>) {
|
||||||
|
self.0.set(Some(toplevel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let selector = Rc::new(Selector(Default::default()));
|
||||||
|
ds.seat.select_toplevel(selector.clone());
|
||||||
|
|
||||||
|
client.compare_screenshot("1", false).await?;
|
||||||
|
|
||||||
|
ds.mouse.abs(
|
||||||
|
&ds.connector,
|
||||||
|
win2pos.0 as f64 + 2.0,
|
||||||
|
win2pos.1 as f64 + 2.0,
|
||||||
|
);
|
||||||
|
run.sync().await;
|
||||||
|
|
||||||
|
client.compare_screenshot("2", false).await?;
|
||||||
|
|
||||||
|
ds.kb.press(1);
|
||||||
|
run.sync().await;
|
||||||
|
tassert!(selector.0.get().is_none());
|
||||||
|
|
||||||
|
ds.seat.select_toplevel(selector.clone());
|
||||||
|
|
||||||
|
client.compare_screenshot("3", false).await?;
|
||||||
|
|
||||||
|
ds.mouse.click(BTN_LEFT);
|
||||||
|
|
||||||
|
client.compare_screenshot("4", false).await?;
|
||||||
|
|
||||||
|
let tl = selector.0.get().expect("no toplevel selected");
|
||||||
|
tassert_eq!(tl.node_id(), win2.tl.server.node_id);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
BIN
src/it/tests/t0042_toplevel_select/screenshot_1.qoi
Normal file
BIN
src/it/tests/t0042_toplevel_select/screenshot_1.qoi
Normal file
Binary file not shown.
BIN
src/it/tests/t0042_toplevel_select/screenshot_2.qoi
Normal file
BIN
src/it/tests/t0042_toplevel_select/screenshot_2.qoi
Normal file
Binary file not shown.
BIN
src/it/tests/t0042_toplevel_select/screenshot_3.qoi
Normal file
BIN
src/it/tests/t0042_toplevel_select/screenshot_3.qoi
Normal file
Binary file not shown.
BIN
src/it/tests/t0042_toplevel_select/screenshot_4.qoi
Normal file
BIN
src/it/tests/t0042_toplevel_select/screenshot_4.qoi
Normal file
Binary file not shown.
|
|
@ -4,8 +4,10 @@ use {
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_callback::WlCallback,
|
wl_callback::WlCallback,
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
xdg_surface::XdgSurface, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceBuffer,
|
x_surface::xwindow::Xwindow,
|
||||||
WlSurface,
|
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||||
|
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
SurfaceBuffer, WlSurface,
|
||||||
},
|
},
|
||||||
wp_presentation_feedback::WpPresentationFeedback,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
},
|
},
|
||||||
|
|
@ -15,8 +17,8 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelNodeBase,
|
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||||
WorkspaceNode,
|
ToplevelNodeBase, WorkspaceNode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -212,7 +214,13 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_placeholder(&mut self, placeholder: &PlaceholderNode, x: i32, y: i32) {
|
pub fn render_placeholder(
|
||||||
|
&mut self,
|
||||||
|
placeholder: &PlaceholderNode,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
) {
|
||||||
let pos = placeholder.tl_data().pos.get();
|
let pos = placeholder.tl_data().pos.get();
|
||||||
self.base.fill_boxes(
|
self.base.fill_boxes(
|
||||||
std::slice::from_ref(&pos.at_point(x, y)),
|
std::slice::from_ref(&pos.at_point(x, y)),
|
||||||
|
|
@ -236,6 +244,7 @@ impl Renderer<'_> {
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
self.render_tl_aux(placeholder.tl_data(), bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||||
|
|
@ -302,6 +311,16 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_xwindow(&mut self, tl: &Xwindow, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
|
self.render_surface(&tl.x.surface, x, y, bounds);
|
||||||
|
self.render_tl_aux(tl.tl_data(), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_xdg_toplevel(&mut self, tl: &XdgToplevel, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
|
self.render_xdg_surface(&tl.xdg, x, y, bounds);
|
||||||
|
self.render_tl_aux(tl.tl_data(), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_xdg_surface(
|
pub fn render_xdg_surface(
|
||||||
&mut self,
|
&mut self,
|
||||||
xdg: &XdgSurface,
|
xdg: &XdgSurface,
|
||||||
|
|
@ -316,6 +335,21 @@ impl Renderer<'_> {
|
||||||
self.render_surface(surface, x, y, bounds);
|
self.render_surface(surface, x, y, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_tl_aux(&mut self, tl_data: &ToplevelData, bounds: Option<&Rect>) {
|
||||||
|
self.render_highlight(tl_data, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_highlight(&mut self, tl_data: &ToplevelData, bounds: Option<&Rect>) {
|
||||||
|
if tl_data.render_highlight.get() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(bounds) = bounds else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let color = self.state.theme.colors.highlight.get();
|
||||||
|
self.base.fill_boxes(slice::from_ref(bounds), &color);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
|
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
let (x, y) = self.base.scale_point(x, y);
|
let (x, y) = self.base.scale_point(x, y);
|
||||||
self.render_surface_scaled(surface, x, y, None, bounds, false);
|
self.render_surface_scaled(surface, x, y, None, bounds, false);
|
||||||
|
|
|
||||||
13
src/theme.rs
13
src/theme.rs
|
|
@ -137,7 +137,7 @@ impl From<jay_config::theme::Color> for Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! colors {
|
macro_rules! colors {
|
||||||
($($name:ident = ($r:expr, $g:expr, $b:expr),)*) => {
|
($($name:ident = $colors:tt,)*) => {
|
||||||
pub struct ThemeColors {
|
pub struct ThemeColors {
|
||||||
$(
|
$(
|
||||||
pub $name: Cell<Color>,
|
pub $name: Cell<Color>,
|
||||||
|
|
@ -157,12 +157,18 @@ macro_rules! colors {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
$(
|
$(
|
||||||
$name: Cell::new(Color::from_rgb($r, $g, $b)),
|
$name: Cell::new(colors!(@colors $colors)),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
(@colors ($r:expr, $g:expr, $b:expr)) => {
|
||||||
|
Color::from_rgb($r, $g, $b)
|
||||||
|
};
|
||||||
|
(@colors ($r:expr, $g:expr, $b:expr, $a:expr)) => {
|
||||||
|
Color::from_rgba_straight($r, $g, $b, $a)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
colors! {
|
colors! {
|
||||||
|
|
@ -180,6 +186,7 @@ colors! {
|
||||||
bar_background = (0x00, 0x00, 0x00),
|
bar_background = (0x00, 0x00, 0x00),
|
||||||
bar_text = (0xff, 0xff, 0xff),
|
bar_text = (0xff, 0xff, 0xff),
|
||||||
attention_requested_background = (0x23, 0x09, 0x2c),
|
attention_requested_background = (0x23, 0x09, 0x2c),
|
||||||
|
highlight = (0x9d, 0x28, 0xc6, 0x7f),
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! sizes {
|
macro_rules! sizes {
|
||||||
|
|
|
||||||
19
src/tree.rs
19
src/tree.rs
|
|
@ -100,6 +100,12 @@ impl FindTreeResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum FindTreeUsecase {
|
||||||
|
None,
|
||||||
|
SelectToplevel,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Node: 'static {
|
pub trait Node: 'static {
|
||||||
fn node_id(&self) -> NodeId;
|
fn node_id(&self) -> NodeId;
|
||||||
fn node_seat_state(&self) -> &NodeSeatState;
|
fn node_seat_state(&self) -> &NodeSeatState;
|
||||||
|
|
@ -122,10 +128,17 @@ pub trait Node: 'static {
|
||||||
let _ = active;
|
let _ = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
let _ = x;
|
let _ = x;
|
||||||
let _ = y;
|
let _ = y;
|
||||||
let _ = tree;
|
let _ = tree;
|
||||||
|
let _ = usecase;
|
||||||
FindTreeResult::Other
|
FindTreeResult::Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,6 +309,10 @@ pub trait Node: 'static {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
// TYPE CHECKERS
|
// TYPE CHECKERS
|
||||||
|
|
||||||
fn node_is_container(&self) -> bool {
|
fn node_is_container(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
text::{self, TextTexture},
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FoundNode, Node,
|
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FindTreeUsecase,
|
||||||
NodeId, ToplevelData, ToplevelNode, ToplevelNodeBase, WorkspaceNode,
|
FoundNode, Node, NodeId, ToplevelData, ToplevelNode, ToplevelNodeBase, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
|
@ -1131,7 +1131,13 @@ impl Node for ContainerNode {
|
||||||
self.toplevel_data.update_self_active(self, active);
|
self.toplevel_data.update_self_active(self, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
let mut recurse = |content: Rect, child: NodeRef<ContainerChild>| {
|
let mut recurse = |content: Rect, child: NodeRef<ContainerChild>| {
|
||||||
if content.contains(x, y) {
|
if content.contains(x, y) {
|
||||||
let (x, y) = content.translate(x, y);
|
let (x, y) = content.translate(x, y);
|
||||||
|
|
@ -1140,7 +1146,7 @@ impl Node for ContainerNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
child.node.node_find_tree_at(x, y, tree);
|
child.node.node_find_tree_at(x, y, tree, usecase);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(child) = self.mono_child.get() {
|
if let Some(child) = self.mono_child.get() {
|
||||||
|
|
@ -1329,6 +1335,10 @@ impl Node for ContainerNode {
|
||||||
fn node_is_container(&self) -> bool {
|
fn node_is_container(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainingNode for ContainerNode {
|
impl ContainingNode for ContainerNode {
|
||||||
|
|
@ -1549,6 +1559,10 @@ impl ToplevelNodeBase for ContainerNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_admits_children(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn direction_to_split(dir: Direction) -> (ContainerSplit, bool) {
|
fn direction_to_split(dir: Direction) -> (ContainerSplit, bool) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, FindTreeResult, FoundNode, Node, NodeId, OutputNode, StackedNode,
|
walker::NodeVisitor, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
|
OutputNode, StackedNode,
|
||||||
},
|
},
|
||||||
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
||||||
},
|
},
|
||||||
|
|
@ -109,7 +110,13 @@ impl Node for DisplayNode {
|
||||||
self.extents.get()
|
self.extents.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
let outputs = self.outputs.lock();
|
let outputs = self.outputs.lock();
|
||||||
for output in outputs.values() {
|
for output in outputs.values() {
|
||||||
let pos = output.global.pos.get();
|
let pos = output.global.pos.get();
|
||||||
|
|
@ -120,7 +127,7 @@ impl Node for DisplayNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
output.node_find_tree_at(x, y, tree);
|
output.node_find_tree_at(x, y, tree, usecase);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
text::{self, TextTexture},
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FoundNode, Node,
|
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FindTreeUsecase,
|
||||||
NodeId, StackedNode, ToplevelNode, WorkspaceNode,
|
FoundNode, Node, NodeId, StackedNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, double_click_state::DoubleClickState,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, double_click_state::DoubleClickState,
|
||||||
|
|
@ -437,7 +437,13 @@ impl Node for FloatNode {
|
||||||
self.update_child_title(title);
|
self.update_child_title(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let th = theme.sizes.title_height.get();
|
let th = theme.sizes.title_height.get();
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
|
|
@ -459,7 +465,7 @@ impl Node for FloatNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
child.node_find_tree_at(x, y, tree)
|
child.node_find_tree_at(x, y, tree, usecase)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_child_active_changed(self: Rc<Self>, _child: &dyn Node, active: bool, _depth: u32) {
|
fn node_child_active_changed(self: Rc<Self>, _child: &dyn Node, active: bool, _depth: u32) {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
text::{self, TextTexture},
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, Direction, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode,
|
walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node,
|
||||||
|
NodeId, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||||
|
|
@ -470,14 +471,20 @@ impl OutputNode {
|
||||||
y: i32,
|
y: i32,
|
||||||
layers: &[u32],
|
layers: &[u32],
|
||||||
tree: &mut Vec<FoundNode>,
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
) -> FindTreeResult {
|
) -> FindTreeResult {
|
||||||
|
if usecase == FindTreeUsecase::SelectToplevel {
|
||||||
|
return FindTreeResult::Other;
|
||||||
|
}
|
||||||
let len = tree.len();
|
let len = tree.len();
|
||||||
for layer in layers.iter().copied() {
|
for layer in layers.iter().copied() {
|
||||||
for surface in self.layers[layer as usize].rev_iter() {
|
for surface in self.layers[layer as usize].rev_iter() {
|
||||||
let pos = surface.output_position();
|
let pos = surface.output_position();
|
||||||
if pos.contains(x, y) {
|
if pos.contains(x, y) {
|
||||||
let (x, y) = pos.translate(x, y);
|
let (x, y) = pos.translate(x, y);
|
||||||
if surface.node_find_tree_at(x, y, tree) == FindTreeResult::AcceptsInput {
|
if surface.node_find_tree_at(x, y, tree, usecase)
|
||||||
|
== FindTreeResult::AcceptsInput
|
||||||
|
{
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
tree.truncate(len);
|
tree.truncate(len);
|
||||||
|
|
@ -627,20 +634,28 @@ impl Node for OutputNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, mut y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
mut y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
if self.state.lock.locked.get() {
|
if self.state.lock.locked.get() {
|
||||||
if let Some(ls) = self.lock_surface.get() {
|
if usecase != FindTreeUsecase::SelectToplevel {
|
||||||
tree.push(FoundNode {
|
if let Some(ls) = self.lock_surface.get() {
|
||||||
node: ls.clone(),
|
tree.push(FoundNode {
|
||||||
x,
|
node: ls.clone(),
|
||||||
y,
|
x,
|
||||||
});
|
y,
|
||||||
return ls.node_find_tree_at(x, y, tree);
|
});
|
||||||
|
return ls.node_find_tree_at(x, y, tree, usecase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let res = self.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree);
|
let res = self.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree, usecase);
|
||||||
if res.accepts_input() {
|
if res.accepts_input() {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -665,7 +680,7 @@ impl Node for OutputNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
match stacked.node_find_tree_at(x, y, tree) {
|
match stacked.node_find_tree_at(x, y, tree, usecase) {
|
||||||
FindTreeResult::AcceptsInput => {
|
FindTreeResult::AcceptsInput => {
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
|
|
@ -685,7 +700,7 @@ impl Node for OutputNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
fs.tl_as_node().node_find_tree_at(x, y, tree)
|
fs.tl_as_node().node_find_tree_at(x, y, tree, usecase)
|
||||||
} else {
|
} else {
|
||||||
let bar_height = self.state.theme.sizes.title_height.get() + 1;
|
let bar_height = self.state.theme.sizes.title_height.get() + 1;
|
||||||
if y >= bar_height {
|
if y >= bar_height {
|
||||||
|
|
@ -697,10 +712,10 @@ impl Node for OutputNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
ws.node_find_tree_at(x, y, tree);
|
ws.node_find_tree_at(x, y, tree, usecase);
|
||||||
}
|
}
|
||||||
if tree.len() == len {
|
if tree.len() == len {
|
||||||
self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree);
|
self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree, usecase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FindTreeResult::AcceptsInput
|
FindTreeResult::AcceptsInput
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
text::{self, TextTexture},
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor,
|
||||||
ToplevelNode, ToplevelNodeBase,
|
ToplevelData, ToplevelNode, ToplevelNodeBase,
|
||||||
},
|
},
|
||||||
utils::{errorfmt::ErrorFmt, smallmap::SmallMap},
|
utils::{errorfmt::ErrorFmt, smallmap::SmallMap},
|
||||||
},
|
},
|
||||||
|
|
@ -116,12 +116,18 @@ impl Node for PlaceholderNode {
|
||||||
self.toplevel.update_self_active(self, active);
|
self.toplevel.update_self_active(self, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, _x: i32, _y: i32, _tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
_x: i32,
|
||||||
|
_y: i32,
|
||||||
|
_tree: &mut Vec<FoundNode>,
|
||||||
|
_usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
FindTreeResult::AcceptsInput
|
FindTreeResult::AcceptsInput
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, _bounds: Option<&Rect>) {
|
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
renderer.render_placeholder(self, x, y);
|
renderer.render_placeholder(self, x, y, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_client(&self) -> Option<Rc<Client>> {
|
fn node_client(&self) -> Option<Rc<Client>> {
|
||||||
|
|
@ -140,6 +146,10 @@ impl Node for PlaceholderNode {
|
||||||
fn node_is_placeholder(&self) -> bool {
|
fn node_is_placeholder(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelNodeBase for PlaceholderNode {
|
impl ToplevelNodeBase for PlaceholderNode {
|
||||||
|
|
@ -173,4 +183,8 @@ impl ToplevelNodeBase for PlaceholderNode {
|
||||||
fn tl_last_active_child(self: Rc<Self>) -> Rc<dyn ToplevelNode> {
|
fn tl_last_active_child(self: Rc<Self>) -> Rc<dyn ToplevelNode> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_admits_children(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use {
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
numcell::NumCell,
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
threshold_counter::ThresholdCounter,
|
threshold_counter::ThresholdCounter,
|
||||||
toplevel_identifier::{toplevel_identifier, ToplevelIdentifier},
|
toplevel_identifier::{toplevel_identifier, ToplevelIdentifier},
|
||||||
|
|
@ -173,6 +174,8 @@ pub trait ToplevelNodeBase: Node {
|
||||||
fn tl_restack_popups(&self) {
|
fn tl_restack_popups(&self) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_admits_children(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FullscreenedData {
|
pub struct FullscreenedData {
|
||||||
|
|
@ -203,6 +206,7 @@ pub struct ToplevelData {
|
||||||
pub identifier: Cell<ToplevelIdentifier>,
|
pub identifier: Cell<ToplevelIdentifier>,
|
||||||
pub handles:
|
pub handles:
|
||||||
CopyHashMap<(ClientId, ExtForeignToplevelHandleV1Id), Rc<ExtForeignToplevelHandleV1>>,
|
CopyHashMap<(ClientId, ExtForeignToplevelHandleV1Id), Rc<ExtForeignToplevelHandleV1>>,
|
||||||
|
pub render_highlight: NumCell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelData {
|
impl ToplevelData {
|
||||||
|
|
@ -229,6 +233,7 @@ impl ToplevelData {
|
||||||
app_id: Default::default(),
|
app_id: Default::default(),
|
||||||
identifier: Cell::new(toplevel_identifier()),
|
identifier: Cell::new(toplevel_identifier()),
|
||||||
handles: Default::default(),
|
handles: Default::default(),
|
||||||
|
render_highlight: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ use {
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction,
|
container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction,
|
||||||
FindTreeResult, FoundNode, Node, NodeId, NodeVisitorBase, OutputNode, StackedNode,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitorBase, OutputNode,
|
||||||
ToplevelNode,
|
StackedNode, ToplevelNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
|
@ -224,14 +224,20 @@ impl Node for WorkspaceNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
|
fn node_find_tree_at(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
tree: &mut Vec<FoundNode>,
|
||||||
|
usecase: FindTreeUsecase,
|
||||||
|
) -> FindTreeResult {
|
||||||
if let Some(n) = self.container.get() {
|
if let Some(n) = self.container.get() {
|
||||||
tree.push(FoundNode {
|
tree.push(FoundNode {
|
||||||
node: n.clone(),
|
node: n.clone(),
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
});
|
});
|
||||||
n.node_find_tree_at(x, y, tree);
|
n.node_find_tree_at(x, y, tree, usecase);
|
||||||
}
|
}
|
||||||
FindTreeResult::AcceptsInput
|
FindTreeResult::AcceptsInput
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ pub struct Theme {
|
||||||
pub separator_color: Option<Color>,
|
pub separator_color: Option<Color>,
|
||||||
pub unfocused_title_bg_color: Option<Color>,
|
pub unfocused_title_bg_color: Option<Color>,
|
||||||
pub unfocused_title_text_color: Option<Color>,
|
pub unfocused_title_text_color: Option<Color>,
|
||||||
|
pub highlight_color: Option<Color>,
|
||||||
pub border_width: Option<i32>,
|
pub border_width: Option<i32>,
|
||||||
pub title_height: Option<i32>,
|
pub title_height: Option<i32>,
|
||||||
pub font: Option<String>,
|
pub font: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ impl Parser for ThemeParser<'_> {
|
||||||
separator_color,
|
separator_color,
|
||||||
unfocused_title_bg_color,
|
unfocused_title_bg_color,
|
||||||
unfocused_title_text_color,
|
unfocused_title_text_color,
|
||||||
|
highlight_color,
|
||||||
border_width,
|
border_width,
|
||||||
title_height,
|
title_height,
|
||||||
font,
|
font,
|
||||||
|
|
@ -77,6 +78,7 @@ impl Parser for ThemeParser<'_> {
|
||||||
opt(val("separator-color")),
|
opt(val("separator-color")),
|
||||||
opt(val("unfocused-title-bg-color")),
|
opt(val("unfocused-title-bg-color")),
|
||||||
opt(val("unfocused-title-text-color")),
|
opt(val("unfocused-title-text-color")),
|
||||||
|
opt(val("highlight-color")),
|
||||||
recover(opt(s32("border-width"))),
|
recover(opt(s32("border-width"))),
|
||||||
recover(opt(s32("title-height"))),
|
recover(opt(s32("title-height"))),
|
||||||
recover(opt(str("font"))),
|
recover(opt(str("font"))),
|
||||||
|
|
@ -111,6 +113,7 @@ impl Parser for ThemeParser<'_> {
|
||||||
separator_color: color!(separator_color),
|
separator_color: color!(separator_color),
|
||||||
unfocused_title_bg_color: color!(unfocused_title_bg_color),
|
unfocused_title_bg_color: color!(unfocused_title_bg_color),
|
||||||
unfocused_title_text_color: color!(unfocused_title_text_color),
|
unfocused_title_text_color: color!(unfocused_title_text_color),
|
||||||
|
highlight_color: color!(highlight_color),
|
||||||
border_width: border_width.despan(),
|
border_width: border_width.despan(),
|
||||||
title_height: title_height.despan(),
|
title_height: title_height.despan(),
|
||||||
font: font.map(|f| f.value.to_string()),
|
font: font.map(|f| f.value.to_string()),
|
||||||
|
|
|
||||||
|
|
@ -665,6 +665,7 @@ impl State {
|
||||||
color!(SEPARATOR_COLOR, separator_color);
|
color!(SEPARATOR_COLOR, separator_color);
|
||||||
color!(UNFOCUSED_TITLE_BACKGROUND_COLOR, unfocused_title_bg_color);
|
color!(UNFOCUSED_TITLE_BACKGROUND_COLOR, unfocused_title_bg_color);
|
||||||
color!(UNFOCUSED_TITLE_TEXT_COLOR, unfocused_title_text_color);
|
color!(UNFOCUSED_TITLE_TEXT_COLOR, unfocused_title_text_color);
|
||||||
|
color!(HIGHLIGHT_COLOR, highlight_color);
|
||||||
macro_rules! size {
|
macro_rules! size {
|
||||||
($sized:ident, $field:ident) => {
|
($sized:ident, $field:ident) => {
|
||||||
if let Some(size) = theme.$field {
|
if let Some(size) = theme.$field {
|
||||||
|
|
|
||||||
|
|
@ -1166,6 +1166,10 @@
|
||||||
"description": "The text color of unfocused titles.",
|
"description": "The text color of unfocused titles.",
|
||||||
"$ref": "#/$defs/Color"
|
"$ref": "#/$defs/Color"
|
||||||
},
|
},
|
||||||
|
"highlight-color": {
|
||||||
|
"description": "Color used to highlight parts of the UI.",
|
||||||
|
"$ref": "#/$defs/Color"
|
||||||
|
},
|
||||||
"border-width": {
|
"border-width": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "The width of borders between windows.",
|
"description": "The width of borders between windows.",
|
||||||
|
|
|
||||||
|
|
@ -2472,6 +2472,12 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [Color](#types-Color).
|
The value of this field should be a [Color](#types-Color).
|
||||||
|
|
||||||
|
- `highlight-color` (optional):
|
||||||
|
|
||||||
|
Color used to highlight parts of the UI.
|
||||||
|
|
||||||
|
The value of this field should be a [Color](#types-Color).
|
||||||
|
|
||||||
- `border-width` (optional):
|
- `border-width` (optional):
|
||||||
|
|
||||||
The width of borders between windows.
|
The width of borders between windows.
|
||||||
|
|
|
||||||
|
|
@ -1602,6 +1602,10 @@ Theme:
|
||||||
ref: Color
|
ref: Color
|
||||||
required: false
|
required: false
|
||||||
description: The text color of unfocused titles.
|
description: The text color of unfocused titles.
|
||||||
|
highlight-color:
|
||||||
|
ref: Color
|
||||||
|
required: false
|
||||||
|
description: Color used to highlight parts of the UI.
|
||||||
border-width:
|
border-width:
|
||||||
kind: number
|
kind: number
|
||||||
integer_only: true
|
integer_only: true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue