Merge pull request #521 from mahkoh/jorth/focus-history
seat: add focus history
This commit is contained in:
commit
7f48424e29
41 changed files with 883 additions and 135 deletions
|
|
@ -15,8 +15,8 @@ use {
|
||||||
client::{Client, ClientCriterion, ClientMatcher, MatchedClient},
|
client::{Client, ClientCriterion, ClientMatcher, MatchedClient},
|
||||||
exec::Command,
|
exec::Command,
|
||||||
input::{
|
input::{
|
||||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile,
|
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, Timeline,
|
||||||
capability::Capability, clickmethod::ClickMethod,
|
acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod,
|
||||||
},
|
},
|
||||||
keyboard::{
|
keyboard::{
|
||||||
Keymap,
|
Keymap,
|
||||||
|
|
@ -364,6 +364,21 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::GrabKb { kb, grab });
|
self.send(&ClientMessage::GrabKb { kb, grab });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn seat_focus_history(&self, seat: Seat, timeline: Timeline) {
|
||||||
|
self.send(&ClientMessage::SeatFocusHistory { seat, timeline });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_focus_history_set_only_visible(&self, seat: Seat, only_visible: bool) {
|
||||||
|
self.send(&ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_focus_history_set_same_workspace(&self, seat: Seat, same_workspace: bool) {
|
||||||
|
self.send(&ClientMessage::SeatFocusHistorySetSameWorkspace {
|
||||||
|
seat,
|
||||||
|
same_workspace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn seat_focus(&self, seat: Seat, direction: Direction) {
|
pub fn seat_focus(&self, seat: Seat, direction: Direction) {
|
||||||
self.send(&ClientMessage::SeatFocus { seat, direction });
|
self.send(&ClientMessage::SeatFocus { seat, direction });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use {
|
||||||
Axis, Direction, PciId, Workspace,
|
Axis, Direction, PciId, Workspace,
|
||||||
client::{Client, ClientMatcher},
|
client::{Client, ClientMatcher},
|
||||||
input::{
|
input::{
|
||||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile,
|
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, Timeline,
|
||||||
capability::Capability, clickmethod::ClickMethod,
|
acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod,
|
||||||
},
|
},
|
||||||
keyboard::{Keymap, mods::Modifiers, syms::KeySym},
|
keyboard::{Keymap, mods::Modifiers, syms::KeySym},
|
||||||
logging::LogLevel,
|
logging::LogLevel,
|
||||||
|
|
@ -725,6 +725,18 @@ pub enum ClientMessage<'a> {
|
||||||
show: bool,
|
show: bool,
|
||||||
},
|
},
|
||||||
GetShowBar,
|
GetShowBar,
|
||||||
|
SeatFocusHistory {
|
||||||
|
seat: Seat,
|
||||||
|
timeline: Timeline,
|
||||||
|
},
|
||||||
|
SeatFocusHistorySetOnlyVisible {
|
||||||
|
seat: Seat,
|
||||||
|
only_visible: bool,
|
||||||
|
},
|
||||||
|
SeatFocusHistorySetSameWorkspace {
|
||||||
|
seat: Seat,
|
||||||
|
same_workspace: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,13 @@ impl InputDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A direction in a timeline.
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
|
pub enum Timeline {
|
||||||
|
Older,
|
||||||
|
Newer,
|
||||||
|
}
|
||||||
|
|
||||||
/// A seat.
|
/// A seat.
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
pub struct Seat(pub u64);
|
pub struct Seat(pub u64);
|
||||||
|
|
@ -273,6 +280,29 @@ impl Seat {
|
||||||
get!().unbind(self, mod_sym.into())
|
get!().unbind(self, mod_sym.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Moves the focus in the focus history.
|
||||||
|
pub fn focus_history(self, timeline: Timeline) {
|
||||||
|
get!().seat_focus_history(self, timeline)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures whether the focus history only includes visible windows.
|
||||||
|
///
|
||||||
|
/// If this is `false`, then hidden windows will be made visible before moving the
|
||||||
|
/// focus to them.
|
||||||
|
///
|
||||||
|
/// The default is `false`.
|
||||||
|
pub fn focus_history_set_only_visible(self, only_visible: bool) {
|
||||||
|
get!().seat_focus_history_set_only_visible(self, only_visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures whether the focus history only includes windows on the same workspace
|
||||||
|
/// as the currently focused window.
|
||||||
|
///
|
||||||
|
/// The default is `false`.
|
||||||
|
pub fn focus_history_set_same_workspace(self, same_workspace: bool) {
|
||||||
|
get!().seat_focus_history_set_same_workspace(self, same_workspace)
|
||||||
|
}
|
||||||
|
|
||||||
/// Moves the keyboard focus of the seat in the specified direction.
|
/// Moves the keyboard focus of the seat in the specified direction.
|
||||||
pub fn focus(self, direction: Direction) {
|
pub fn focus(self, direction: Direction) {
|
||||||
get!().seat_focus(self, direction)
|
get!().seat_focus(self, direction)
|
||||||
|
|
|
||||||
|
|
@ -752,6 +752,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
is_dummy: true,
|
is_dummy: true,
|
||||||
output: CloneCell::new(dummy_output.clone()),
|
output: CloneCell::new(dummy_output.clone()),
|
||||||
|
output_id: Cell::new(dummy_output.id),
|
||||||
position: Default::default(),
|
position: Default::default(),
|
||||||
container: Default::default(),
|
container: Default::default(),
|
||||||
stacked: Default::default(),
|
stacked: Default::default(),
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ use {
|
||||||
Axis, Direction, Workspace,
|
Axis, Direction, Workspace,
|
||||||
client::{Client as ConfigClient, ClientMatcher},
|
client::{Client as ConfigClient, ClientMatcher},
|
||||||
input::{
|
input::{
|
||||||
FocusFollowsMouseMode, InputDevice, Seat,
|
FocusFollowsMouseMode, InputDevice, Seat, Timeline,
|
||||||
acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT, AccelProfile},
|
acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT, AccelProfile},
|
||||||
capability::{
|
capability::{
|
||||||
CAP_GESTURE, CAP_KEYBOARD, CAP_POINTER, CAP_SWITCH, CAP_TABLET_PAD,
|
CAP_GESTURE, CAP_KEYBOARD, CAP_POINTER, CAP_SWITCH, CAP_TABLET_PAD,
|
||||||
|
|
@ -2150,11 +2150,41 @@ impl ConfigProxyHandler {
|
||||||
.set(matcher, (m, tile_state));
|
.set(matcher, (m, tile_state));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
|
fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
|
||||||
self.get_seat(seat)?.set_pointer_revert_key(key);
|
self.get_seat(seat)?.set_pointer_revert_key(key);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_seat_focus_history(&self, seat: Seat, timeline: Timeline) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
match timeline {
|
||||||
|
Timeline::Older => seat.focus_prev(),
|
||||||
|
Timeline::Newer => seat.focus_next(),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_focus_history_set_only_visible(
|
||||||
|
&self,
|
||||||
|
seat: Seat,
|
||||||
|
visible: bool,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.focus_history_set_visible(visible);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_focus_history_set_same_workspace(
|
||||||
|
&self,
|
||||||
|
seat: Seat,
|
||||||
|
same_workspace: bool,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.focus_history_set_same_workspace(same_workspace);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn spaces_change(&self) {
|
fn spaces_change(&self) {
|
||||||
struct V;
|
struct V;
|
||||||
impl NodeVisitorBase for V {
|
impl NodeVisitorBase for V {
|
||||||
|
|
@ -2997,6 +3027,18 @@ impl ConfigProxyHandler {
|
||||||
.wrn("get_content_type")?,
|
.wrn("get_content_type")?,
|
||||||
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
|
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
|
||||||
ClientMessage::GetShowBar => self.handle_get_show_bar(),
|
ClientMessage::GetShowBar => self.handle_get_show_bar(),
|
||||||
|
ClientMessage::SeatFocusHistory { seat, timeline } => self
|
||||||
|
.handle_seat_focus_history(seat, timeline)
|
||||||
|
.wrn("seat_focus_history")?,
|
||||||
|
ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible } => self
|
||||||
|
.handle_seat_focus_history_set_only_visible(seat, only_visible)
|
||||||
|
.wrn("seat_focus_history_set_only_visible")?,
|
||||||
|
ClientMessage::SeatFocusHistorySetSameWorkspace {
|
||||||
|
seat,
|
||||||
|
same_workspace,
|
||||||
|
} => self
|
||||||
|
.handle_seat_focus_history_set_same_workspace(seat, same_workspace)
|
||||||
|
.wrn("seat_focus_history_set_same_workspace")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use {
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
tree::NodeLocation,
|
||||||
wire::{ExtSessionLockV1Id, ext_session_lock_v1::*},
|
wire::{ExtSessionLockV1Id, ext_session_lock_v1::*},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{cell::Cell, rc::Rc},
|
||||||
|
|
@ -87,7 +88,7 @@ impl ExtSessionLockV1RequestHandler for ExtSessionLockV1 {
|
||||||
node.set_lock_surface(Some(new.clone()));
|
node.set_lock_surface(Some(new.clone()));
|
||||||
let pos = node.global.pos.get();
|
let pos = node.global.pos.get();
|
||||||
new.change_extents(pos);
|
new.change_extents(pos);
|
||||||
new.surface.set_output(&node);
|
new.surface.set_output(&node, NodeLocation::Output(node.id));
|
||||||
self.client.state.tree_changed();
|
self.client.state.tree_changed();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ use {
|
||||||
},
|
},
|
||||||
wl_output::WlOutputGlobal,
|
wl_output::WlOutputGlobal,
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
|
event_handling::FocusHistoryData,
|
||||||
gesture_owner::GestureOwnerHolder,
|
gesture_owner::GestureOwnerHolder,
|
||||||
kb_owner::KbOwnerHolder,
|
kb_owner::KbOwnerHolder,
|
||||||
pointer_owner::PointerOwnerHolder,
|
pointer_owner::PointerOwnerHolder,
|
||||||
|
|
@ -79,13 +80,19 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::{DeviceHandlerData, State},
|
state::{DeviceHandlerData, State},
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId, OutputNode,
|
ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId, NodeLocation,
|
||||||
ToplevelNode, WorkspaceNode, generic_node_visitor, toplevel_create_split,
|
OutputNode, ToplevelNode, WorkspaceNode, generic_node_visitor, toplevel_create_split,
|
||||||
toplevel_parent_container, toplevel_set_floating, toplevel_set_workspace,
|
toplevel_parent_container, toplevel_set_floating, toplevel_set_workspace,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, bindings::PerClientBindings, clonecell::CloneCell,
|
asyncevent::AsyncEvent,
|
||||||
copyhashmap::CopyHashMap, linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq,
|
bindings::PerClientBindings,
|
||||||
|
clonecell::CloneCell,
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||||
|
numcell::NumCell,
|
||||||
|
on_drop::OnDrop,
|
||||||
|
rc_eq::{rc_eq, rc_weak_eq},
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
|
|
@ -218,6 +225,11 @@ pub struct WlSeatGlobal {
|
||||||
keyboard_node_serial: Cell<u64>,
|
keyboard_node_serial: Cell<u64>,
|
||||||
tray_popups: CopyHashMap<(TrayItemId, XdgPopupId), Rc<dyn DynTrayItem>>,
|
tray_popups: CopyHashMap<(TrayItemId, XdgPopupId), Rc<dyn DynTrayItem>>,
|
||||||
revert_key: Cell<KeySym>,
|
revert_key: Cell<KeySym>,
|
||||||
|
last_focus_location: Cell<Option<NodeLocation>>,
|
||||||
|
focus_history: LinkedList<FocusHistoryData>,
|
||||||
|
focus_history_rotate: NumCell<u64>,
|
||||||
|
focus_history_visible_only: Cell<bool>,
|
||||||
|
focus_history_same_workspace: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||||
|
|
@ -292,6 +304,11 @@ impl WlSeatGlobal {
|
||||||
ui_drag_highlight: Default::default(),
|
ui_drag_highlight: Default::default(),
|
||||||
tray_popups: Default::default(),
|
tray_popups: Default::default(),
|
||||||
revert_key: Cell::new(SYM_Escape),
|
revert_key: Cell::new(SYM_Escape),
|
||||||
|
last_focus_location: Default::default(),
|
||||||
|
focus_history: Default::default(),
|
||||||
|
focus_history_rotate: Default::default(),
|
||||||
|
focus_history_visible_only: Cell::new(false),
|
||||||
|
focus_history_same_workspace: Cell::new(false),
|
||||||
});
|
});
|
||||||
slf.pointer_cursor.set_owner(slf.clone());
|
slf.pointer_cursor.set_owner(slf.clone());
|
||||||
let seat = slf.clone();
|
let seat = slf.clone();
|
||||||
|
|
@ -649,6 +666,141 @@ impl WlSeatGlobal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_last_focus_on_workspace(&self, ws: &WorkspaceNode) -> Option<Rc<dyn Node>> {
|
||||||
|
let mut node = self.focus_history.last()?;
|
||||||
|
loop {
|
||||||
|
if let Some(node) = node.node.upgrade()
|
||||||
|
&& let Some(NodeLocation::Workspace(_, new)) = node.node_location()
|
||||||
|
&& new == ws.id
|
||||||
|
{
|
||||||
|
return Some(node);
|
||||||
|
}
|
||||||
|
node = node.prev()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_focus_history(
|
||||||
|
&self,
|
||||||
|
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||||
|
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||||
|
) -> Option<(Rc<dyn Node>, bool)> {
|
||||||
|
let original = self.keyboard_node.get();
|
||||||
|
let mut output = None;
|
||||||
|
let mut workspace = None;
|
||||||
|
if let Some(old) = original.node_location() {
|
||||||
|
match old {
|
||||||
|
NodeLocation::Workspace(o, w) => {
|
||||||
|
workspace = Some(w);
|
||||||
|
output = Some(o);
|
||||||
|
}
|
||||||
|
NodeLocation::Output(o) => {
|
||||||
|
output = Some(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output.is_none() || workspace.is_none())
|
||||||
|
&& let Some(old) = self.last_focus_location.get()
|
||||||
|
{
|
||||||
|
match old {
|
||||||
|
NodeLocation::Workspace(o, w) => {
|
||||||
|
workspace = workspace.or(Some(w));
|
||||||
|
output = output.or(Some(o));
|
||||||
|
}
|
||||||
|
NodeLocation::Output(o) => {
|
||||||
|
output = output.or(Some(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if workspace.is_none()
|
||||||
|
&& let Some(output) = original.node_output()
|
||||||
|
&& let Some(ws) = output.workspace.get()
|
||||||
|
{
|
||||||
|
workspace = Some(ws.id);
|
||||||
|
}
|
||||||
|
let matches = |node: &FocusHistoryData| {
|
||||||
|
let visible = node.visible.get();
|
||||||
|
if self.focus_history_visible_only.get() && !visible {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let node = node.node.upgrade()?;
|
||||||
|
if self.focus_history_same_workspace.get() {
|
||||||
|
let new = node.node_location()?;
|
||||||
|
let o = match new {
|
||||||
|
NodeLocation::Workspace(o, w) => {
|
||||||
|
if workspace != Some(w) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
o
|
||||||
|
}
|
||||||
|
NodeLocation::Output(o) => o,
|
||||||
|
};
|
||||||
|
if output != Some(o) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((node, visible))
|
||||||
|
};
|
||||||
|
let node = original.node_seat_state().get_focus_history(self);
|
||||||
|
if let Some(mut node) = node {
|
||||||
|
loop {
|
||||||
|
node = match next(&node) {
|
||||||
|
Some(n) => n,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
if let Some(matches) = matches(&node) {
|
||||||
|
return Some(matches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut node = first(&self.focus_history)?;
|
||||||
|
loop {
|
||||||
|
if rc_weak_eq(&original, &node.node) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(matches) = matches(&node) {
|
||||||
|
return Some(matches);
|
||||||
|
}
|
||||||
|
node = next(&node)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_history(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||||
|
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||||
|
) {
|
||||||
|
let Some((node, visible)) = self.get_focus_history(next, first) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.focus_history_rotate.fetch_add(1);
|
||||||
|
let _reset = OnDrop(|| {
|
||||||
|
self.focus_history_rotate.fetch_sub(1);
|
||||||
|
});
|
||||||
|
if !visible {
|
||||||
|
node.clone().node_make_visible();
|
||||||
|
if !node.node_visible() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.focus_node(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_prev(self: &Rc<Self>) {
|
||||||
|
self.focus_history(|s| s.prev(), |l| l.last());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_next(self: &Rc<Self>) {
|
||||||
|
self.focus_history(|s| s.next(), |l| l.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_history_set_visible(&self, visible: bool) {
|
||||||
|
self.focus_history_visible_only.set(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
|
||||||
|
self.focus_history_same_workspace.set(same_workspace);
|
||||||
|
}
|
||||||
|
|
||||||
fn set_selection_<T, X, S>(
|
fn set_selection_<T, X, S>(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
|
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
|
||||||
|
|
@ -717,7 +869,9 @@ impl WlSeatGlobal {
|
||||||
serial: u64,
|
serial: u64,
|
||||||
) -> Result<(), WlSeatError> {
|
) -> Result<(), WlSeatError> {
|
||||||
if let Some(icon) = &icon {
|
if let Some(icon) = &icon {
|
||||||
icon.surface().set_output(&self.pointer_cursor.output());
|
let output = self.pointer_cursor.output();
|
||||||
|
icon.surface()
|
||||||
|
.set_output(&output, NodeLocation::Output(output.id));
|
||||||
}
|
}
|
||||||
self.pointer_owner
|
self.pointer_owner
|
||||||
.start_drag(self, origin, source, icon, serial)
|
.start_drag(self, origin, source, icon, serial)
|
||||||
|
|
@ -1082,7 +1236,8 @@ impl WlSeatGlobal {
|
||||||
impl CursorUserOwner for WlSeatGlobal {
|
impl CursorUserOwner for WlSeatGlobal {
|
||||||
fn output_changed(&self, output: &Rc<OutputNode>) {
|
fn output_changed(&self, output: &Rc<OutputNode>) {
|
||||||
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
||||||
dnd.surface().set_output(output);
|
dnd.surface()
|
||||||
|
.set_output(output, NodeLocation::Output(output.id));
|
||||||
}
|
}
|
||||||
if let Some(drag) = self.pointer_owner.toplevel_drag()
|
if let Some(drag) = self.pointer_owner.toplevel_drag()
|
||||||
&& let Some(tl) = drag.toplevel.get()
|
&& let Some(tl) = drag.toplevel.get()
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,10 @@ use {
|
||||||
state::DeviceHandlerData,
|
state::DeviceHandlerData,
|
||||||
tree::{Direction, Node, ToplevelNode},
|
tree::{Direction, Node, ToplevelNode},
|
||||||
utils::{
|
utils::{
|
||||||
bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap,
|
bitflags::BitflagsExt,
|
||||||
|
hash_map_ext::HashMapExt,
|
||||||
|
linkedlist::{LinkedNode, NodeRef},
|
||||||
|
smallmap::{SmallMap, SmallMapMut},
|
||||||
syncqueue::SyncQueue,
|
syncqueue::SyncQueue,
|
||||||
},
|
},
|
||||||
wire::WlDataOfferId,
|
wire::WlDataOfferId,
|
||||||
|
|
@ -56,13 +59,20 @@ use {
|
||||||
kbvm::{ModifierMask, state_machine::Event},
|
kbvm::{ModifierMask, state_machine::Event},
|
||||||
linearize::LinearizeExt,
|
linearize::LinearizeExt,
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::{cell::RefCell, collections::hash_map::Entry, mem, rc::Rc},
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::hash_map::Entry,
|
||||||
|
mem,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NodeSeatState {
|
pub struct NodeSeatState {
|
||||||
pointer_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
pointer_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
kb_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
kb_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
|
no_focus_history: Cell<bool>,
|
||||||
|
kb_focus_histories: RefCell<SmallMapMut<SeatId, LinkedNode<FocusHistoryData>, 1>>,
|
||||||
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
touch_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
touch_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
|
|
@ -72,6 +82,11 @@ pub struct NodeSeatState {
|
||||||
ui_drags: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
ui_drags: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FocusHistoryData {
|
||||||
|
pub visible: Cell<bool>,
|
||||||
|
pub node: Weak<dyn Node>,
|
||||||
|
}
|
||||||
|
|
||||||
impl NodeSeatState {
|
impl NodeSeatState {
|
||||||
pub(super) fn enter(&self, seat: &Rc<WlSeatGlobal>) {
|
pub(super) fn enter(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
self.pointer_foci.insert(seat.id, seat.clone());
|
self.pointer_foci.insert(seat.id, seat.clone());
|
||||||
|
|
@ -81,7 +96,26 @@ impl NodeSeatState {
|
||||||
self.pointer_foci.remove(&seat.id);
|
self.pointer_foci.remove(&seat.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn focus(&self, seat: &Rc<WlSeatGlobal>) -> bool {
|
pub fn disable_focus_history(&self) {
|
||||||
|
self.no_focus_history.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn focus(&self, node: &Rc<dyn Node>, seat: &Rc<WlSeatGlobal>) -> bool {
|
||||||
|
if !self.no_focus_history.get() {
|
||||||
|
let hist = &mut *self.kb_focus_histories.borrow_mut();
|
||||||
|
let hist = hist.get_or_insert_with(seat.id, || {
|
||||||
|
seat.focus_history.add_last(FocusHistoryData {
|
||||||
|
visible: Cell::new(node.node_visible()),
|
||||||
|
node: Rc::downgrade(node),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if seat.focus_history_rotate.is_zero() {
|
||||||
|
seat.last_focus_location.set(node.node_location());
|
||||||
|
seat.focus_history.add_last_existing(hist);
|
||||||
|
} else {
|
||||||
|
seat.focus_history.rotate_last(hist);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.kb_foci.insert(seat.id, seat.clone());
|
self.kb_foci.insert(seat.id, seat.clone());
|
||||||
self.kb_foci.len() == 1
|
self.kb_foci.len() == 1
|
||||||
}
|
}
|
||||||
|
|
@ -179,6 +213,10 @@ impl NodeSeatState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_node(&self, node: &dyn Node) {
|
pub fn destroy_node(&self, node: &dyn Node) {
|
||||||
|
for (_, entry) in self.kb_focus_histories.borrow_mut().iter_mut() {
|
||||||
|
entry.visible.set(false);
|
||||||
|
entry.detach();
|
||||||
|
}
|
||||||
self.destroy_node2(node, true);
|
self.destroy_node2(node, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,11 +261,22 @@ impl NodeSeatState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
||||||
|
for (_, entry) in self.kb_focus_histories.borrow_mut().iter_mut() {
|
||||||
|
entry.visible.set(visible);
|
||||||
|
}
|
||||||
if !visible {
|
if !visible {
|
||||||
self.destroy_node2(node, false);
|
self.destroy_node2(node, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_focus_history(
|
||||||
|
&self,
|
||||||
|
seat: &WlSeatGlobal,
|
||||||
|
) -> Option<NodeRef<FocusHistoryData>> {
|
||||||
|
let hist = &*self.kb_focus_histories.borrow();
|
||||||
|
Some(hist.get(&seat.id)?.to_ref())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_seat_remove(&self, seat: &WlSeatGlobal) {
|
pub fn on_seat_remove(&self, seat: &WlSeatGlobal) {
|
||||||
self.kb_foci.remove(&seat.id);
|
self.kb_foci.remove(&seat.id);
|
||||||
self.pointer_foci.remove(&seat.id);
|
self.pointer_foci.remove(&seat.id);
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ impl KbOwner for DefaultKbOwner {
|
||||||
old.node_active_changed(false);
|
old.node_active_changed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.node_seat_state().focus(seat) {
|
if node.node_seat_state().focus(&node, seat) {
|
||||||
node.node_active_changed(true);
|
node.node_active_changed(true);
|
||||||
}
|
}
|
||||||
// log::info!("focus {}", node.node_id());
|
// log::info!("focus {}", node.node_id());
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,8 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{
|
tree::{
|
||||||
BeforeLatchListener, BeforeLatchResult, ContainerNode, FindTreeResult, FoundNode,
|
BeforeLatchListener, BeforeLatchResult, ContainerNode, FindTreeResult, FoundNode,
|
||||||
LatchListener, Node, NodeId, NodeVisitor, NodeVisitorBase, OutputNode, PlaceholderNode,
|
LatchListener, Node, NodeId, NodeLocation, NodeVisitor, NodeVisitorBase, OutputNode,
|
||||||
PresentationListener, ToplevelNode, VblankListener,
|
PlaceholderNode, PresentationListener, ToplevelNode, VblankListener,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
|
|
@ -319,6 +319,7 @@ pub struct WlSurface {
|
||||||
idle_inhibitors: SmallMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>, 1>,
|
idle_inhibitors: SmallMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>, 1>,
|
||||||
viewporter: CloneCell<Option<Rc<WpViewport>>>,
|
viewporter: CloneCell<Option<Rc<WpViewport>>>,
|
||||||
output: CloneCell<Rc<OutputNode>>,
|
output: CloneCell<Rc<OutputNode>>,
|
||||||
|
location: Cell<NodeLocation>,
|
||||||
fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>,
|
fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>,
|
||||||
pub constraints: SmallMap<SeatId, Rc<SeatConstraint>, 1>,
|
pub constraints: SmallMap<SeatId, Rc<SeatConstraint>, 1>,
|
||||||
xwayland_serial: Cell<Option<u64>>,
|
xwayland_serial: Cell<Option<u64>>,
|
||||||
|
|
@ -623,6 +624,7 @@ pub struct StackElement {
|
||||||
|
|
||||||
impl WlSurface {
|
impl WlSurface {
|
||||||
pub fn new(id: WlSurfaceId, client: &Rc<Client>, version: Version, slf: &Weak<Self>) -> Self {
|
pub fn new(id: WlSurfaceId, client: &Rc<Client>, version: Version, slf: &Weak<Self>) -> Self {
|
||||||
|
let dummy_output = client.state.dummy_output.get().unwrap();
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
node_id: client.state.node_ids.next(),
|
node_id: client.state.node_ids.next(),
|
||||||
|
|
@ -662,7 +664,8 @@ impl WlSurface {
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
idle_inhibitors: Default::default(),
|
idle_inhibitors: Default::default(),
|
||||||
viewporter: Default::default(),
|
viewporter: Default::default(),
|
||||||
output: CloneCell::new(client.state.dummy_output.get().unwrap()),
|
location: Cell::new(NodeLocation::Output(dummy_output.id)),
|
||||||
|
output: CloneCell::new(dummy_output),
|
||||||
fractional_scale: Default::default(),
|
fractional_scale: Default::default(),
|
||||||
constraints: Default::default(),
|
constraints: Default::default(),
|
||||||
xwayland_serial: Default::default(),
|
xwayland_serial: Default::default(),
|
||||||
|
|
@ -715,7 +718,8 @@ impl WlSurface {
|
||||||
self.output.get()
|
self.output.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
pub fn set_output(&self, output: &Rc<OutputNode>, location: NodeLocation) {
|
||||||
|
self.location.set(location);
|
||||||
let old = self.output.set(output.clone());
|
let old = self.output.set(output.clone());
|
||||||
if old.id == output.id {
|
if old.id == output.id {
|
||||||
return;
|
return;
|
||||||
|
|
@ -737,7 +741,7 @@ impl WlSurface {
|
||||||
let children = self.children.borrow_mut();
|
let children = self.children.borrow_mut();
|
||||||
if let Some(children) = &*children {
|
if let Some(children) = &*children {
|
||||||
for ss in children.subsurfaces.values() {
|
for ss in children.subsurfaces.values() {
|
||||||
ss.surface.set_output(output);
|
ss.surface.set_output(output, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1788,6 +1792,10 @@ impl Node for WlSurface {
|
||||||
Some(self.output.get())
|
Some(self.output.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
Some(self.location.get())
|
||||||
|
}
|
||||||
|
|
||||||
fn node_active_changed(&self, active: bool) {
|
fn node_active_changed(&self, active: bool) {
|
||||||
if let Some(tl) = self.toplevel.get() {
|
if let Some(tl) = self.toplevel.get() {
|
||||||
tl.tl_surface_active_changed(active);
|
tl.tl_surface_active_changed(active);
|
||||||
|
|
@ -1810,6 +1818,12 @@ impl Node for WlSurface {
|
||||||
self.ext.get().tray_item()
|
self.ext.get().tray_item()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
if let Some(tl) = self.toplevel.get() {
|
||||||
|
tl.node_make_visible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_key(
|
fn node_on_key(
|
||||||
&self,
|
&self,
|
||||||
seat: &WlSeatGlobal,
|
seat: &WlSeatGlobal,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
tree::{Node, NodeVisitorBase, OutputNode},
|
tree::{Node, NodeLocation, NodeVisitorBase, OutputNode},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
@ -133,7 +133,8 @@ impl Cursor for CursorSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
self.surface.set_output(output);
|
self.surface
|
||||||
|
.set_output(output, NodeLocation::Output(output.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set(self: Rc<Self>) {
|
fn handle_set(self: Rc<Self>) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ use {
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode},
|
tree::{
|
||||||
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation, NodeVisitor,
|
||||||
|
OutputNode,
|
||||||
|
},
|
||||||
utils::numcell::NumCell,
|
utils::numcell::NumCell,
|
||||||
wire::{ExtSessionLockSurfaceV1Id, WlSurfaceId, ext_session_lock_surface_v1::*},
|
wire::{ExtSessionLockSurfaceV1Id, WlSurfaceId, ext_session_lock_surface_v1::*},
|
||||||
},
|
},
|
||||||
|
|
@ -131,6 +134,10 @@ impl Node for ExtSessionLockSurfaceV1 {
|
||||||
self.output.node()
|
self.output.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(
|
fn node_find_tree_at(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use {
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
tree::{
|
tree::{
|
||||||
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation, NodeVisitor,
|
||||||
StackedNode,
|
OutputNode, StackedNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
|
@ -212,6 +212,10 @@ impl<T: TrayItem> XdgPopupParent for Popup<T> {
|
||||||
self.parent.node_visible()
|
self.parent.node_visible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
fn tray_item(&self) -> Option<TrayItemId> {
|
fn tray_item(&self) -> Option<TrayItemId> {
|
||||||
Some(self.parent.data().tray_item_id)
|
Some(self.parent.data().tray_item_id)
|
||||||
}
|
}
|
||||||
|
|
@ -300,6 +304,10 @@ impl<T: TrayItem> Node for T {
|
||||||
self.data().output.node()
|
self.data().output.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.data().surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(
|
fn node_find_tree_at(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
@ -328,7 +336,8 @@ fn install<T: TrayItem>(item: &Rc<T>) -> Result<(), TrayItemError> {
|
||||||
data.surface.ext.set(item.clone());
|
data.surface.ext.set(item.clone());
|
||||||
data.surface.set_visible(false);
|
data.surface.set_visible(false);
|
||||||
if let Some(node) = data.output.node() {
|
if let Some(node) = data.output.node() {
|
||||||
data.surface.set_output(&node);
|
data.surface
|
||||||
|
.set_output(&node, NodeLocation::Output(node.id));
|
||||||
item.send_initial_configure();
|
item.send_initial_configure();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,8 @@ impl WlSubsurface {
|
||||||
update_children_attach(self)?;
|
update_children_attach(self)?;
|
||||||
let (x, y) = self.parent.buffer_abs_pos.get().position();
|
let (x, y) = self.parent.buffer_abs_pos.get().position();
|
||||||
self.surface.set_absolute_position(x, y);
|
self.surface.set_absolute_position(x, y);
|
||||||
self.surface.set_output(&self.parent.output.get());
|
self.surface
|
||||||
|
.set_output(&self.parent.output.get(), self.parent.location.get());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
NodeVisitor, OutputNode, StackedNode, TileDragDestination, ToplevelData, ToplevelNode,
|
NodeLocation, NodeVisitor, OutputNode, StackedNode, TileDragDestination, ToplevelData,
|
||||||
ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_destination,
|
ToplevelNode, ToplevelNodeBase, ToplevelType, WorkspaceNode,
|
||||||
|
default_tile_drag_destination,
|
||||||
},
|
},
|
||||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
||||||
wire::WlSurfaceId,
|
wire::WlSurfaceId,
|
||||||
|
|
@ -370,6 +371,10 @@ impl Node for Xwindow {
|
||||||
self.toplevel_data.output_opt()
|
self.toplevel_data.output_opt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.x.surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
||||||
seat.focus_toplevel(self.clone());
|
seat.focus_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -407,6 +412,10 @@ impl Node for Xwindow {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
self.toplevel_data.make_visible(&*self);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||||
seat.enter_toplevel(self.clone());
|
seat.enter_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -446,7 +455,7 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
self.x.surface.set_output(&ws.output.get());
|
self.x.surface.set_output(&ws.output.get(), ws.location());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_change_extents_impl(self: Rc<Self>, rect: &Rect) {
|
fn tl_change_extents_impl(self: Rc<Self>, rect: &Rect) {
|
||||||
|
|
@ -456,7 +465,9 @@ impl ToplevelNodeBase for Xwindow {
|
||||||
if self.data.info.override_redirect.get() {
|
if self.data.info.override_redirect.get() {
|
||||||
let (x, y) = rect.center();
|
let (x, y) = rect.center();
|
||||||
let output = self.data.state.find_closest_output(x, y).0;
|
let output = self.data.state.find_closest_output(x, y).0;
|
||||||
self.x.surface.set_output(&output);
|
self.x
|
||||||
|
.surface
|
||||||
|
.set_output(&output, NodeLocation::Output(output.id));
|
||||||
} else {
|
} else {
|
||||||
self.data
|
self.data
|
||||||
.state
|
.state
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ use {
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
tree::{FindTreeResult, FoundNode, Node, OutputNode, StackedNode, WorkspaceNode},
|
tree::{
|
||||||
|
FindTreeResult, FoundNode, Node, NodeLocation, OutputNode, StackedNode, WorkspaceNode,
|
||||||
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
|
@ -144,6 +146,12 @@ impl XdgPopupParent for Popup {
|
||||||
self.parent.surface.visible.get()
|
self.parent.surface.visible.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>) {
|
||||||
|
if let Some(ext) = self.parent.ext.get() {
|
||||||
|
ext.make_visible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn tray_item(&self) -> Option<TrayItemId> {
|
fn tray_item(&self) -> Option<TrayItemId> {
|
||||||
self.parent.clone().tray_item()
|
self.parent.clone().tray_item()
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +199,8 @@ pub trait XdgSurfaceExt: Debug {
|
||||||
fn tray_item(&self) -> Option<TrayItemId> {
|
fn tray_item(&self) -> Option<TrayItemId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XdgSurface {
|
impl XdgSurface {
|
||||||
|
|
@ -233,7 +243,7 @@ impl XdgSurface {
|
||||||
|
|
||||||
fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
|
fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
self.workspace.set(Some(ws.clone()));
|
self.workspace.set(Some(ws.clone()));
|
||||||
self.surface.set_output(&ws.output.get());
|
self.surface.set_output(&ws.output.get(), ws.location());
|
||||||
let pu = self.popups.lock();
|
let pu = self.popups.lock();
|
||||||
for pu in pu.values() {
|
for pu in pu.values() {
|
||||||
pu.popup.xdg.set_workspace(ws);
|
pu.popup.xdg.set_workspace(ws);
|
||||||
|
|
@ -241,7 +251,8 @@ impl XdgSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
self.surface.set_output(output);
|
self.surface
|
||||||
|
.set_output(output, NodeLocation::Output(output.id));
|
||||||
let pu = self.popups.lock();
|
let pu = self.popups.lock();
|
||||||
for pu in pu.values() {
|
for pu in pu.values() {
|
||||||
pu.popup.xdg.set_output(output);
|
pu.popup.xdg.set_output(output);
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{
|
tree::{
|
||||||
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation, NodeVisitor,
|
||||||
StackedNode,
|
OutputNode, StackedNode,
|
||||||
},
|
},
|
||||||
utils::clonecell::CloneCell,
|
utils::clonecell::CloneCell,
|
||||||
wire::{XdgPopupId, xdg_popup::*},
|
wire::{XdgPopupId, xdg_popup::*},
|
||||||
|
|
@ -45,6 +45,7 @@ pub trait XdgPopupParent {
|
||||||
fn has_workspace_link(&self) -> bool;
|
fn has_workspace_link(&self) -> bool;
|
||||||
fn post_commit(&self);
|
fn post_commit(&self);
|
||||||
fn visible(&self) -> bool;
|
fn visible(&self) -> bool;
|
||||||
|
fn make_visible(self: Rc<Self>);
|
||||||
fn tray_item(&self) -> Option<TrayItemId> {
|
fn tray_item(&self) -> Option<TrayItemId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -319,6 +320,10 @@ impl Node for XdgPopup {
|
||||||
self.xdg.workspace.get().map(|w| w.output.get())
|
self.xdg.workspace.get().map(|w| w.output.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.xdg.surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(
|
fn node_find_tree_at(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
@ -340,6 +345,12 @@ impl Node for XdgPopup {
|
||||||
Some(self.xdg.surface.client.clone())
|
Some(self.xdg.surface.client.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
if let Some(parent) = self.parent.get() {
|
||||||
|
parent.make_visible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||||
seat.enter_popup(&self);
|
seat.enter_popup(&self);
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +437,10 @@ impl XdgSurfaceExt for XdgPopup {
|
||||||
fn tray_item(&self) -> Option<TrayItemId> {
|
fn tray_item(&self) -> Option<TrayItemId> {
|
||||||
self.parent.get()?.tray_item()
|
self.parent.get()?.tray_item()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>) {
|
||||||
|
self.node_make_visible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
NodeLocation, NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
||||||
ToplevelNodeBase, ToplevelNodeId, ToplevelType, WorkspaceNode,
|
ToplevelNodeBase, ToplevelNodeId, ToplevelType, WorkspaceNode,
|
||||||
default_tile_drag_destination,
|
default_tile_drag_destination,
|
||||||
},
|
},
|
||||||
|
|
@ -512,7 +512,7 @@ impl XdgToplevel {
|
||||||
self.extents_changed();
|
self.extents_changed();
|
||||||
if let Some(workspace) = self.xdg.workspace.get() {
|
if let Some(workspace) = self.xdg.workspace.get() {
|
||||||
let output = workspace.output.get();
|
let output = workspace.output.get();
|
||||||
surface.set_output(&output);
|
surface.set_output(&output, workspace.location());
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
// let seats = surface.client.state.globals.lock_seats();
|
// let seats = surface.client.state.globals.lock_seats();
|
||||||
|
|
@ -573,6 +573,10 @@ impl Node for XdgToplevel {
|
||||||
self.toplevel_data.output_opt()
|
self.toplevel_data.output_opt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.xdg.surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
||||||
seat.focus_toplevel(self.clone());
|
seat.focus_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -606,6 +610,10 @@ impl Node for XdgToplevel {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
self.toplevel_data.make_visible(&*self)
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||||
seat.enter_toplevel(self.clone());
|
seat.enter_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -776,6 +784,10 @@ impl XdgSurfaceExt for XdgToplevel {
|
||||||
.state
|
.state
|
||||||
.damage(self.node_absolute_position());
|
.damage(self.node_absolute_position());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>) {
|
||||||
|
self.node_make_visible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{
|
tree::{
|
||||||
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation, NodeVisitor,
|
||||||
StackedNode,
|
OutputNode, StackedNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
bitflags::BitflagsExt,
|
bitflags::BitflagsExt,
|
||||||
|
|
@ -187,7 +187,8 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
}
|
}
|
||||||
self.surface.ext.set(self.clone());
|
self.surface.ext.set(self.clone());
|
||||||
if let Some(output) = self.output.node() {
|
if let Some(output) = self.output.node() {
|
||||||
self.surface.set_output(&output);
|
self.surface
|
||||||
|
.set_output(&output, NodeLocation::Output(output.id));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -658,6 +659,10 @@ impl Node for ZwlrLayerSurfaceV1 {
|
||||||
self.output.node()
|
self.output.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.surface.node_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(
|
fn node_find_tree_at(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
@ -719,6 +724,10 @@ impl XdgPopupParent for Popup {
|
||||||
fn visible(&self) -> bool {
|
fn visible(&self) -> bool {
|
||||||
self.parent.node_visible()
|
self.parent.node_visible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_visible(self: Rc<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -250,10 +250,10 @@ impl ExtWorkspaceManagerV1RequestHandler for ExtWorkspaceManagerV1 {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let output = ws.output.get();
|
let output = ws.output.get();
|
||||||
output.show_workspace(&ws);
|
let seat = self.client.state.seat_queue.last().as_deref().cloned();
|
||||||
ws.flush_jay_workspaces();
|
self.client
|
||||||
output.schedule_update_render_data();
|
.state
|
||||||
self.client.state.tree_changed();
|
.show_workspace2(seat.as_ref(), &output, &ws);
|
||||||
}
|
}
|
||||||
WorkspaceChange::AssignWorkspace(w, o) => {
|
WorkspaceChange::AssignWorkspace(w, o) => {
|
||||||
let Some(ws) = w.get() else {
|
let Some(ws) = w.get() else {
|
||||||
|
|
|
||||||
70
src/state.rs
70
src/state.rs
|
|
@ -853,48 +853,52 @@ impl State {
|
||||||
node.node_do_focus(&seat, Direction::Unspecified);
|
node.node_do_focus(&seat, Direction::Unspecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_workspace(&self, seat: &Rc<WlSeatGlobal>, name: &str) {
|
pub fn show_workspace2(
|
||||||
let (output, ws) = match self.workspaces.get(name) {
|
&self,
|
||||||
Some(ws) => {
|
seat: Option<&Rc<WlSeatGlobal>>,
|
||||||
let output = ws.output.get();
|
output: &Rc<OutputNode>,
|
||||||
let mut pinned_is_focused = false;
|
ws: &Rc<WorkspaceNode>,
|
||||||
for pinned in output.pinned.iter() {
|
) {
|
||||||
pinned
|
let mut pinned_is_focused = false;
|
||||||
.deref()
|
if let Some(seat) = seat {
|
||||||
.clone()
|
for pinned in output.pinned.iter() {
|
||||||
.node_visit(&mut generic_node_visitor(|node| {
|
pinned
|
||||||
node.node_seat_state().for_each_kb_focus(|s| {
|
.deref()
|
||||||
pinned_is_focused |= s.id() == seat.id();
|
.clone()
|
||||||
});
|
.node_visit(&mut generic_node_visitor(|node| {
|
||||||
}));
|
node.node_seat_state().for_each_kb_focus(|s| {
|
||||||
}
|
pinned_is_focused |= s.id() == seat.id();
|
||||||
let did_change = output.show_workspace(&ws);
|
});
|
||||||
if !pinned_is_focused {
|
}));
|
||||||
ws.clone().node_do_focus(seat, Direction::Unspecified);
|
|
||||||
}
|
|
||||||
if !did_change {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(output, ws)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
let did_change = output.show_workspace(&ws);
|
||||||
|
if !pinned_is_focused && let Some(seat) = seat {
|
||||||
|
ws.clone().node_do_focus(seat, Direction::Unspecified);
|
||||||
|
}
|
||||||
|
if !did_change {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ws.flush_jay_workspaces();
|
||||||
|
if !output.is_dummy {
|
||||||
|
output.schedule_update_render_data();
|
||||||
|
self.tree_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_workspace(&self, seat: &Rc<WlSeatGlobal>, name: &str) {
|
||||||
|
let ws = match self.workspaces.get(name) {
|
||||||
|
Some(ws) => ws,
|
||||||
_ => {
|
_ => {
|
||||||
let output = seat.get_output();
|
let output = seat.get_output();
|
||||||
if output.is_dummy {
|
if output.is_dummy {
|
||||||
log::warn!("Not showing workspace because seat is on dummy output");
|
log::warn!("Not showing workspace because seat is on dummy output");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ws = output.create_workspace(name);
|
output.create_workspace(name)
|
||||||
output.show_workspace(&ws);
|
|
||||||
(output, ws)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ws.flush_jay_workspaces();
|
self.show_workspace2(Some(seat), &ws.output.get(), &ws);
|
||||||
output.schedule_update_render_data();
|
|
||||||
self.tree_changed();
|
|
||||||
// let seats = self.globals.seats.lock();
|
|
||||||
// for seat in seats.values() {
|
|
||||||
// seat.workspace_changed(&output);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn float_map_ws(&self) -> Rc<WorkspaceNode> {
|
pub fn float_map_ws(&self) -> Rc<WorkspaceNode> {
|
||||||
|
|
|
||||||
11
src/tree.rs
11
src/tree.rs
|
|
@ -115,6 +115,12 @@ pub enum FindTreeUsecase {
|
||||||
SelectWorkspace,
|
SelectWorkspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum NodeLocation {
|
||||||
|
Workspace(OutputNodeId, WorkspaceNodeId),
|
||||||
|
Output(OutputNodeId),
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -123,6 +129,7 @@ pub trait Node: 'static {
|
||||||
fn node_visible(&self) -> bool;
|
fn node_visible(&self) -> bool;
|
||||||
fn node_absolute_position(&self) -> Rect;
|
fn node_absolute_position(&self) -> Rect;
|
||||||
fn node_output(&self) -> Option<Rc<OutputNode>>;
|
fn node_output(&self) -> Option<Rc<OutputNode>>;
|
||||||
|
fn node_location(&self) -> Option<NodeLocation>;
|
||||||
|
|
||||||
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
||||||
let _ = child;
|
let _ = child;
|
||||||
|
|
@ -183,6 +190,10 @@ pub trait Node: 'static {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
// EVENT HANDLERS
|
// EVENT HANDLERS
|
||||||
|
|
||||||
fn node_on_key(
|
fn node_on_key(
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ use {
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FloatNode, FoundNode, Node,
|
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FloatNode, FoundNode, Node,
|
||||||
NodeId, OutputNode, TddType, TileDragDestination, ToplevelData, ToplevelNode,
|
NodeId, NodeLocation, OutputNode, TddType, TileDragDestination, ToplevelData,
|
||||||
ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_bounds,
|
ToplevelNode, ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_bounds,
|
||||||
toplevel_set_floating, walker::NodeVisitor,
|
toplevel_set_floating, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
|
|
@ -129,6 +129,7 @@ pub struct ContainerNode {
|
||||||
focus_history: LinkedList<NodeRef<ContainerChild>>,
|
focus_history: LinkedList<NodeRef<ContainerChild>>,
|
||||||
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
|
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
|
||||||
workspace: CloneCell<Rc<WorkspaceNode>>,
|
workspace: CloneCell<Rc<WorkspaceNode>>,
|
||||||
|
location: Cell<NodeLocation>,
|
||||||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||||
state: Rc<State>,
|
state: Rc<State>,
|
||||||
pub render_data: RefCell<ContainerRenderData>,
|
pub render_data: RefCell<ContainerRenderData>,
|
||||||
|
|
@ -235,6 +236,7 @@ impl ContainerNode {
|
||||||
focus_history: Default::default(),
|
focus_history: Default::default(),
|
||||||
child_nodes: RefCell::new(child_nodes),
|
child_nodes: RefCell::new(child_nodes),
|
||||||
workspace: CloneCell::new(workspace.clone()),
|
workspace: CloneCell::new(workspace.clone()),
|
||||||
|
location: Cell::new(workspace.location()),
|
||||||
cursors: RefCell::new(Default::default()),
|
cursors: RefCell::new(Default::default()),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
render_data: Default::default(),
|
render_data: Default::default(),
|
||||||
|
|
@ -1543,6 +1545,14 @@ impl Node for ContainerNode {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_output(&self) -> Option<Rc<OutputNode>> {
|
||||||
|
self.toplevel_data.output_opt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
Some(self.location.get())
|
||||||
|
}
|
||||||
|
|
||||||
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
||||||
if let Some(child) = self.child_nodes.borrow().get(&child.node_id()) {
|
if let Some(child) = self.child_nodes.borrow().get(&child.node_id()) {
|
||||||
self.update_child_title(child, title);
|
self.update_child_title(child, title);
|
||||||
|
|
@ -1626,6 +1636,10 @@ impl Node for ContainerNode {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
self.toplevel_data.make_visible(&*self);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_button(
|
fn node_on_button(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
seat: &Rc<WlSeatGlobal>,
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
|
@ -1675,6 +1689,14 @@ impl Node for ContainerNode {
|
||||||
.node_do_focus(seat, Direction::Unspecified);
|
.node_do_focus(seat, Direction::Unspecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
||||||
|
let mut seats = self.cursors.borrow_mut();
|
||||||
|
let id = CursorType::Seat(seat.id());
|
||||||
|
if let Some(seat_state) = seats.get_mut(&id) {
|
||||||
|
seat_state.op = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
|
||||||
// log::info!("node_on_pointer_enter");
|
// log::info!("node_on_pointer_enter");
|
||||||
self.pointer_move(
|
self.pointer_move(
|
||||||
|
|
@ -1687,14 +1709,6 @@ impl Node for ContainerNode {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
|
||||||
let mut seats = self.cursors.borrow_mut();
|
|
||||||
let id = CursorType::Seat(seat.id());
|
|
||||||
if let Some(seat_state) = seats.get_mut(&id) {
|
|
||||||
seat_state.op = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
|
fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
// log::info!("unfocus");
|
// log::info!("unfocus");
|
||||||
let mut seats = self.cursors.borrow_mut();
|
let mut seats = self.cursors.borrow_mut();
|
||||||
|
|
@ -1781,10 +1795,6 @@ impl Node for ContainerNode {
|
||||||
fn node_is_container(&self) -> bool {
|
fn node_is_container(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_output(&self) -> Option<Rc<OutputNode>> {
|
|
||||||
self.toplevel_data.output_opt()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainingNode for ContainerNode {
|
impl ContainingNode for ContainerNode {
|
||||||
|
|
@ -1913,6 +1923,28 @@ impl ContainingNode for ContainerNode {
|
||||||
self.workspace.get()
|
self.workspace.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_make_visible(self: Rc<Self>, child: &dyn Node) {
|
||||||
|
let Some(child) = self
|
||||||
|
.child_nodes
|
||||||
|
.borrow()
|
||||||
|
.get(&child.node_id())
|
||||||
|
.map(|n| n.to_ref())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.toplevel_data.make_visible(&*self);
|
||||||
|
if !self.node_visible() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(cur) = self.mono_child.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if cur.node.node_id() == child.node.node_id() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.activate_child(&child);
|
||||||
|
}
|
||||||
|
|
||||||
fn cnode_set_child_position(self: Rc<Self>, child: &dyn Node, x: i32, y: i32) {
|
fn cnode_set_child_position(self: Rc<Self>, child: &dyn Node, x: i32, y: i32) {
|
||||||
let Some(parent) = self.toplevel_data.parent.get() else {
|
let Some(parent) = self.toplevel_data.parent.get() else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2084,6 +2116,8 @@ impl ToplevelNodeBase for ContainerNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
|
self.workspace.set(ws.clone());
|
||||||
|
self.location.set(ws.location());
|
||||||
for child in self.children.iter() {
|
for child in self.children.iter() {
|
||||||
child.node.clone().tl_set_workspace(ws);
|
child.node.clone().tl_set_workspace(ws);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub trait ContainingNode: Node {
|
||||||
fn cnode_accepts_child(&self, node: &dyn Node) -> bool;
|
fn cnode_accepts_child(&self, node: &dyn Node) -> bool;
|
||||||
fn cnode_child_attention_request_changed(self: Rc<Self>, child: &dyn Node, set: bool);
|
fn cnode_child_attention_request_changed(self: Rc<Self>, child: &dyn Node, set: bool);
|
||||||
fn cnode_workspace(self: Rc<Self>) -> Rc<WorkspaceNode>;
|
fn cnode_workspace(self: Rc<Self>) -> Rc<WorkspaceNode>;
|
||||||
|
fn cnode_make_visible(self: Rc<Self>, child: &dyn Node);
|
||||||
fn cnode_set_child_position(self: Rc<Self>, child: &dyn Node, x: i32, y: i32) {
|
fn cnode_set_child_position(self: Rc<Self>, child: &dyn Node, x: i32, y: i32) {
|
||||||
let _ = child;
|
let _ = child;
|
||||||
let _ = x;
|
let _ = x;
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, OutputNode, StackedNode,
|
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation, OutputNode,
|
||||||
TileDragDestination, WorkspaceDragDestination, WorkspaceNodeId, walker::NodeVisitor,
|
StackedNode, TileDragDestination, WorkspaceDragDestination, WorkspaceNodeId,
|
||||||
|
walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
|
||||||
},
|
},
|
||||||
|
|
@ -27,14 +28,16 @@ pub struct DisplayNode {
|
||||||
|
|
||||||
impl DisplayNode {
|
impl DisplayNode {
|
||||||
pub fn new(id: NodeId) -> Self {
|
pub fn new(id: NodeId) -> Self {
|
||||||
Self {
|
let slf = Self {
|
||||||
id,
|
id,
|
||||||
extents: Default::default(),
|
extents: Default::default(),
|
||||||
outputs: Default::default(),
|
outputs: Default::default(),
|
||||||
stacked: Default::default(),
|
stacked: Default::default(),
|
||||||
stacked_above_layers: Default::default(),
|
stacked_above_layers: Default::default(),
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
}
|
};
|
||||||
|
slf.seat_state.disable_focus_history();
|
||||||
|
slf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
|
|
@ -150,6 +153,10 @@ impl Node for DisplayNode {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn node_find_tree_at(
|
fn node_find_tree_at(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use {
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
OutputNode, PinnedNode, StackedNode, TileDragDestination, ToplevelNode, WorkspaceNode,
|
NodeLocation, OutputNode, PinnedNode, StackedNode, TileDragDestination, ToplevelNode,
|
||||||
toplevel_set_floating, walker::NodeVisitor,
|
WorkspaceNode, toplevel_set_floating, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, double_click_state::DoubleClickState,
|
asyncevent::AsyncEvent, clonecell::CloneCell, double_click_state::DoubleClickState,
|
||||||
|
|
@ -45,6 +45,7 @@ pub struct FloatNode {
|
||||||
pub workspace_link: Cell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
|
pub workspace_link: Cell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
|
||||||
pub pinned_link: RefCell<Option<LinkedNode<Rc<dyn PinnedNode>>>>,
|
pub pinned_link: RefCell<Option<LinkedNode<Rc<dyn PinnedNode>>>>,
|
||||||
pub workspace: CloneCell<Rc<WorkspaceNode>>,
|
pub workspace: CloneCell<Rc<WorkspaceNode>>,
|
||||||
|
pub location: Cell<NodeLocation>,
|
||||||
pub child: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
pub child: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
||||||
pub active: Cell<bool>,
|
pub active: Cell<bool>,
|
||||||
pub seat_state: NodeSeatState,
|
pub seat_state: NodeSeatState,
|
||||||
|
|
@ -124,6 +125,7 @@ impl FloatNode {
|
||||||
workspace_link: Cell::new(None),
|
workspace_link: Cell::new(None),
|
||||||
pinned_link: RefCell::new(None),
|
pinned_link: RefCell::new(None),
|
||||||
workspace: CloneCell::new(ws.clone()),
|
workspace: CloneCell::new(ws.clone()),
|
||||||
|
location: Cell::new(ws.location()),
|
||||||
child: CloneCell::new(Some(child.clone())),
|
child: CloneCell::new(Some(child.clone())),
|
||||||
active: Cell::new(false),
|
active: Cell::new(false),
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
|
|
@ -423,6 +425,7 @@ impl FloatNode {
|
||||||
self.workspace_link
|
self.workspace_link
|
||||||
.set(Some(ws.stacked.add_last(self.clone())));
|
.set(Some(ws.stacked.add_last(self.clone())));
|
||||||
self.workspace.set(ws.clone());
|
self.workspace.set(ws.clone());
|
||||||
|
self.location.set(ws.location());
|
||||||
if update_visible {
|
if update_visible {
|
||||||
self.stacked_set_visible(ws.float_visible());
|
self.stacked_set_visible(ws.float_visible());
|
||||||
}
|
}
|
||||||
|
|
@ -702,6 +705,10 @@ impl Node for FloatNode {
|
||||||
Some(self.workspace.get().output.get())
|
Some(self.workspace.get().output.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
Some(self.location.get())
|
||||||
|
}
|
||||||
|
|
||||||
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, title: &str) {
|
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, title: &str) {
|
||||||
self.update_child_title(title);
|
self.update_child_title(title);
|
||||||
}
|
}
|
||||||
|
|
@ -745,6 +752,13 @@ impl Node for FloatNode {
|
||||||
renderer.render_floating(self, x, y)
|
renderer.render_floating(self, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
if self.visible.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.workspace.get().cnode_make_visible(&*self);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_button(
|
fn node_on_button(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
seat: &Rc<WlSeatGlobal>,
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
|
@ -897,6 +911,10 @@ impl ContainingNode for FloatNode {
|
||||||
self.workspace.get()
|
self.workspace.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_make_visible(self: Rc<Self>, _child: &dyn Node) {
|
||||||
|
self.node_make_visible();
|
||||||
|
}
|
||||||
|
|
||||||
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
|
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let th = theme.sizes.title_height.get();
|
let th = theme.sizes.title_height.get();
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, PinnedNode,
|
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLocation,
|
||||||
StackedNode, TddType, TileDragDestination, WorkspaceDragDestination, WorkspaceNode,
|
PinnedNode, StackedNode, TddType, TileDragDestination, WorkspaceDragDestination,
|
||||||
WorkspaceNodeId, walker::NodeVisitor,
|
WorkspaceNode, WorkspaceNodeId, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
|
asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
|
||||||
|
|
@ -718,6 +718,7 @@ impl OutputNode {
|
||||||
state: self.state.clone(),
|
state: self.state.clone(),
|
||||||
is_dummy: false,
|
is_dummy: false,
|
||||||
output: CloneCell::new(self.clone()),
|
output: CloneCell::new(self.clone()),
|
||||||
|
output_id: Cell::new(self.id),
|
||||||
position: Cell::new(Default::default()),
|
position: Cell::new(Default::default()),
|
||||||
container: Default::default(),
|
container: Default::default(),
|
||||||
stacked: Default::default(),
|
stacked: Default::default(),
|
||||||
|
|
@ -1080,7 +1081,7 @@ impl OutputNode {
|
||||||
set_layer_visible!(self.layers[3], visible);
|
set_layer_visible!(self.layers[3], visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button(self: Rc<Self>, id: PointerType) {
|
fn button(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, id: PointerType) {
|
||||||
if !self.state.show_bar.get() {
|
if !self.state.show_bar.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1104,10 +1105,7 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.show_workspace(&ws);
|
self.state.show_workspace2(Some(seat), &self, &ws);
|
||||||
ws.flush_jay_workspaces();
|
|
||||||
self.schedule_update_render_data();
|
|
||||||
self.state.tree_changed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_presentation_type(&self) {
|
pub fn update_presentation_type(&self) {
|
||||||
|
|
@ -1476,6 +1474,10 @@ impl Node for OutputNode {
|
||||||
self.global.opt.node()
|
self.global.opt.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
Some(NodeLocation::Output(self.id))
|
||||||
|
}
|
||||||
|
|
||||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
||||||
if self.state.lock.locked.get() {
|
if self.state.lock.locked.get() {
|
||||||
if let Some(lock) = self.lock_surface.get() {
|
if let Some(lock) = self.lock_surface.get() {
|
||||||
|
|
@ -1623,7 +1625,7 @@ impl Node for OutputNode {
|
||||||
self.pointer_down.remove(&seat.id());
|
self.pointer_down.remove(&seat.id());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.button(PointerType::Seat(seat.id()));
|
self.button(seat, PointerType::Seat(seat.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||||
|
|
@ -1653,15 +1655,7 @@ impl Node for OutputNode {
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if !self.show_workspace(&ws) {
|
self.state.show_workspace2(Some(seat), &self, &ws);
|
||||||
return;
|
|
||||||
}
|
|
||||||
ws.flush_jay_workspaces();
|
|
||||||
ws.deref()
|
|
||||||
.clone()
|
|
||||||
.node_do_focus(seat, Direction::Unspecified);
|
|
||||||
self.schedule_update_render_data();
|
|
||||||
self.state.tree_changed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
||||||
|
|
@ -1725,7 +1719,7 @@ impl Node for OutputNode {
|
||||||
self.pointer_move(id, x, y);
|
self.pointer_move(id, x, y);
|
||||||
if let Some(changes) = changes {
|
if let Some(changes) = changes {
|
||||||
if changes.down == Some(true) {
|
if changes.down == Some(true) {
|
||||||
self.button(id);
|
self.button(tool.seat(), id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use {
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
NodeLocation, NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
||||||
ToplevelNodeBase, ToplevelType, default_tile_drag_destination,
|
ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_destination,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, errorfmt::ErrorFmt, on_drop_event::OnDropEvent,
|
asyncevent::AsyncEvent, errorfmt::ErrorFmt, on_drop_event::OnDropEvent,
|
||||||
|
|
@ -35,6 +35,7 @@ pub struct PlaceholderNode {
|
||||||
destroyed: Cell<bool>,
|
destroyed: Cell<bool>,
|
||||||
update_textures_scheduled: Cell<bool>,
|
update_textures_scheduled: Cell<bool>,
|
||||||
state: Rc<State>,
|
state: Rc<State>,
|
||||||
|
location: Cell<Option<NodeLocation>>,
|
||||||
pub textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
pub textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +64,7 @@ impl PlaceholderNode {
|
||||||
destroyed: Default::default(),
|
destroyed: Default::default(),
|
||||||
update_textures_scheduled: Cell::new(false),
|
update_textures_scheduled: Cell::new(false),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
location: Cell::new(node.node_location()),
|
||||||
textures: Default::default(),
|
textures: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +84,7 @@ impl PlaceholderNode {
|
||||||
destroyed: Default::default(),
|
destroyed: Default::default(),
|
||||||
update_textures_scheduled: Default::default(),
|
update_textures_scheduled: Default::default(),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
location: Default::default(),
|
||||||
textures: Default::default(),
|
textures: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +173,14 @@ impl Node for PlaceholderNode {
|
||||||
self.toplevel.pos.get()
|
self.toplevel.pos.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_output(&self) -> Option<Rc<OutputNode>> {
|
||||||
|
self.toplevel.output_opt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
self.location.get()
|
||||||
|
}
|
||||||
|
|
||||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
||||||
seat.focus_toplevel(self.clone());
|
seat.focus_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -200,21 +211,21 @@ impl Node for PlaceholderNode {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
self.toplevel.make_visible(&*self);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||||
seat.enter_toplevel(self.clone());
|
seat.enter_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_is_placeholder(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_output(&self) -> Option<Rc<OutputNode>> {
|
fn node_is_placeholder(&self) -> bool {
|
||||||
self.toplevel.output_opt()
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,6 +234,10 @@ impl ToplevelNodeBase for PlaceholderNode {
|
||||||
&self.toplevel
|
&self.toplevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_set_workspace_ext(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
|
self.location.set(ws.node_location());
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_change_extents_impl(self: Rc<Self>, rect: &Rect) {
|
fn tl_change_extents_impl(self: Rc<Self>, rect: &Rect) {
|
||||||
self.toplevel.pos.set(*rect);
|
self.toplevel.pos.set(*rect);
|
||||||
if let Some(p) = self.toplevel.parent.get() {
|
if let Some(p) = self.toplevel.parent.get() {
|
||||||
|
|
|
||||||
|
|
@ -866,6 +866,15 @@ impl ToplevelData {
|
||||||
self.property_changed(TL_CHANGED_CONTENT_TY);
|
self.property_changed(TL_CHANGED_CONTENT_TY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_visible(&self, slf: &dyn Node) {
|
||||||
|
if self.visible.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(parent) = self.parent.get() {
|
||||||
|
parent.cnode_make_visible(slf);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ToplevelData {
|
impl Drop for ToplevelData {
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ use {
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FloatNode, FoundNode, Node,
|
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FloatNode, FoundNode, Node,
|
||||||
NodeId, NodeVisitorBase, OutputNode, PlaceholderNode, StackedNode, ToplevelNode,
|
NodeId, NodeLocation, NodeVisitorBase, OutputNode, OutputNodeId, PlaceholderNode,
|
||||||
container::ContainerNode, walker::NodeVisitor,
|
StackedNode, ToplevelNode, container::ContainerNode, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
|
@ -49,6 +49,7 @@ pub struct WorkspaceNode {
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
pub is_dummy: bool,
|
pub is_dummy: bool,
|
||||||
pub output: CloneCell<Rc<OutputNode>>,
|
pub output: CloneCell<Rc<OutputNode>>,
|
||||||
|
pub output_id: Cell<OutputNodeId>,
|
||||||
pub position: Cell<Rect>,
|
pub position: Cell<Rect>,
|
||||||
pub container: CloneCell<Option<Rc<ContainerNode>>>,
|
pub container: CloneCell<Option<Rc<ContainerNode>>>,
|
||||||
pub stacked: LinkedList<Rc<dyn StackedNode>>,
|
pub stacked: LinkedList<Rc<dyn StackedNode>>,
|
||||||
|
|
@ -104,6 +105,7 @@ impl WorkspaceNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
|
self.output_id.set(output.id);
|
||||||
let old = self.output.set(output.clone());
|
let old = self.output.set(output.clone());
|
||||||
for wh in self.ext_workspaces.lock().values() {
|
for wh in self.ext_workspaces.lock().values() {
|
||||||
wh.handle_new_output(output);
|
wh.handle_new_output(output);
|
||||||
|
|
@ -113,12 +115,13 @@ impl WorkspaceNode {
|
||||||
}
|
}
|
||||||
self.update_has_captures();
|
self.update_has_captures();
|
||||||
struct OutputSetter<'a> {
|
struct OutputSetter<'a> {
|
||||||
|
ws: &'a WorkspaceNode,
|
||||||
old: &'a Rc<OutputNode>,
|
old: &'a Rc<OutputNode>,
|
||||||
new: &'a Rc<OutputNode>,
|
new: &'a Rc<OutputNode>,
|
||||||
}
|
}
|
||||||
impl NodeVisitorBase for OutputSetter<'_> {
|
impl NodeVisitorBase for OutputSetter<'_> {
|
||||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
node.set_output(self.new);
|
node.set_output(self.new, self.ws.location());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
|
@ -147,6 +150,7 @@ impl WorkspaceNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut visitor = OutputSetter {
|
let mut visitor = OutputSetter {
|
||||||
|
ws: self,
|
||||||
old: &old,
|
old: &old,
|
||||||
new: output,
|
new: output,
|
||||||
};
|
};
|
||||||
|
|
@ -273,6 +277,10 @@ impl WorkspaceNode {
|
||||||
self.output.get().schedule_update_render_data();
|
self.output.get().schedule_update_render_data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn location(&self) -> NodeLocation {
|
||||||
|
NodeLocation::Workspace(self.output_id.get(), self.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for WorkspaceNode {
|
impl Node for WorkspaceNode {
|
||||||
|
|
@ -309,9 +317,17 @@ impl Node for WorkspaceNode {
|
||||||
Some(self.output.get())
|
Some(self.output.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_location(&self) -> Option<NodeLocation> {
|
||||||
|
Some(self.location())
|
||||||
|
}
|
||||||
|
|
||||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
||||||
if let Some(fs) = self.fullscreen.get() {
|
if let Some(fs) = self.fullscreen.get() {
|
||||||
fs.node_do_focus(seat, direction);
|
fs.node_do_focus(seat, direction);
|
||||||
|
} else if self.stacked.is_not_empty()
|
||||||
|
&& let Some(last) = seat.get_last_focus_on_workspace(&self)
|
||||||
|
{
|
||||||
|
seat.focus_node(last);
|
||||||
} else if let Some(container) = self.container.get() {
|
} else if let Some(container) = self.container.get() {
|
||||||
container.node_do_focus(seat, direction);
|
container.node_do_focus(seat, direction);
|
||||||
} else if let Some(float) = self
|
} else if let Some(float) = self
|
||||||
|
|
@ -347,6 +363,13 @@ impl Node for WorkspaceNode {
|
||||||
renderer.render_workspace(self, x, y);
|
renderer.render_workspace(self, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn node_make_visible(self: Rc<Self>) {
|
||||||
|
if self.is_dummy {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.state.show_workspace2(None, &self.output.get(), &self);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
// log::info!("workspace focus");
|
// log::info!("workspace focus");
|
||||||
seat.pointer_cursor().set_known(KnownCursor::Default);
|
seat.pointer_cursor().set_known(KnownCursor::Default);
|
||||||
|
|
@ -422,6 +445,10 @@ impl ContainingNode for WorkspaceNode {
|
||||||
fn cnode_workspace(self: Rc<Self>) -> Rc<WorkspaceNode> {
|
fn cnode_workspace(self: Rc<Self>) -> Rc<WorkspaceNode> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_make_visible(self: Rc<Self>, _child: &dyn Node) {
|
||||||
|
self.node_make_visible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WsMoveConfig {
|
pub struct WsMoveConfig {
|
||||||
|
|
@ -472,14 +499,13 @@ pub fn move_ws_to_output(
|
||||||
&& (config.make_visible_always
|
&& (config.make_visible_always
|
||||||
|| (config.make_visible_if_empty && target.workspace.is_none()));
|
|| (config.make_visible_if_empty && target.workspace.is_none()));
|
||||||
if make_visible {
|
if make_visible {
|
||||||
target.show_workspace(&ws);
|
ws.state.show_workspace2(None, target, &ws);
|
||||||
} else {
|
} else {
|
||||||
ws.set_visible(false);
|
ws.set_visible(false);
|
||||||
}
|
}
|
||||||
ws.flush_jay_workspaces();
|
ws.flush_jay_workspaces();
|
||||||
if let Some(ws) = new_source_ws {
|
if let Some(ws) = new_source_ws {
|
||||||
source.show_workspace(&ws);
|
ws.state.show_workspace2(None, &source, &ws);
|
||||||
ws.flush_jay_workspaces();
|
|
||||||
}
|
}
|
||||||
if !target.is_dummy {
|
if !target.is_dummy {
|
||||||
target.schedule_update_render_data();
|
target.schedule_update_render_data();
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,18 @@ impl<T> LinkedList<T> {
|
||||||
self.root.append_existing(t)
|
self.root.append_existing(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rotate_last(&self, t: &NodeRef<T>) {
|
||||||
|
unsafe {
|
||||||
|
let root = self.root.data.as_ref();
|
||||||
|
root.prev.get().as_ref().next.set(root.next.get());
|
||||||
|
root.next.get().as_ref().prev.set(root.prev.get());
|
||||||
|
root.prev.set(t.data);
|
||||||
|
root.next.set(t.data.as_ref().next.get());
|
||||||
|
t.data.as_ref().next.get().as_ref().prev.set(self.root.data);
|
||||||
|
t.data.as_ref().next.set(self.root.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> LinkedListIter<T> {
|
pub fn iter(&self) -> LinkedListIter<T> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let root = self.root.data.as_ref();
|
let root = self.root.data.as_ref();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
use std::{ops::Deref, rc::Rc};
|
use std::{
|
||||||
|
ops::Deref,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn rc_eq<T: ?Sized>(a: &Rc<T>, b: &Rc<T>) -> bool {
|
pub fn rc_eq<T: ?Sized>(a: &Rc<T>, b: &Rc<T>) -> bool {
|
||||||
Rc::as_ptr(a) as *const u8 == Rc::as_ptr(b) as *const u8
|
Rc::as_ptr(a) as *const u8 == Rc::as_ptr(b) as *const u8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rc_weak_eq<T: ?Sized>(a: &Rc<T>, b: &Weak<T>) -> bool {
|
||||||
|
Rc::as_ptr(a) as *const u8 == b.as_ptr() as *const u8
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RcEq<T>(pub Rc<T>);
|
pub struct RcEq<T>(pub Rc<T>);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use {
|
||||||
color_management::ColorManagement,
|
color_management::ColorManagement,
|
||||||
config::{ConfigParser, ConfigParserError},
|
config::{ConfigParser, ConfigParserError},
|
||||||
float::Float,
|
float::Float,
|
||||||
|
focus_history::FocusHistory,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
toml::{self},
|
toml::{self},
|
||||||
|
|
@ -22,7 +23,7 @@ use {
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
Axis, Direction, Workspace,
|
Axis, Direction, Workspace,
|
||||||
input::{SwitchEvent, acceleration::AccelProfile, clickmethod::ClickMethod},
|
input::{SwitchEvent, Timeline, acceleration::AccelProfile, clickmethod::ClickMethod},
|
||||||
keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym},
|
keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym},
|
||||||
logging::LogLevel,
|
logging::LogLevel,
|
||||||
status::MessageFormat,
|
status::MessageFormat,
|
||||||
|
|
@ -70,6 +71,7 @@ pub enum SimpleCommand {
|
||||||
KillClient,
|
KillClient,
|
||||||
ShowBar(bool),
|
ShowBar(bool),
|
||||||
ToggleBar,
|
ToggleBar,
|
||||||
|
FocusHistory(Timeline),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -486,6 +488,7 @@ pub struct Config {
|
||||||
pub pointer_revert_key: Option<KeySym>,
|
pub pointer_revert_key: Option<KeySym>,
|
||||||
pub use_hardware_cursor: Option<bool>,
|
pub use_hardware_cursor: Option<bool>,
|
||||||
pub show_bar: Option<bool>,
|
pub show_bar: Option<bool>,
|
||||||
|
pub focus_history: Option<FocusHistory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ mod drm_device_match;
|
||||||
mod env;
|
mod env;
|
||||||
pub mod exec;
|
pub mod exec;
|
||||||
pub mod float;
|
pub mod float;
|
||||||
|
pub mod focus_history;
|
||||||
mod format;
|
mod format;
|
||||||
mod gfx_api;
|
mod gfx_api;
|
||||||
mod idle;
|
mod idle;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ use {
|
||||||
jay_config::{
|
jay_config::{
|
||||||
Axis::{Horizontal, Vertical},
|
Axis::{Horizontal, Vertical},
|
||||||
get_workspace,
|
get_workspace,
|
||||||
|
input::Timeline,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
@ -136,6 +137,8 @@ impl ActionParser<'_> {
|
||||||
"show-bar" => ShowBar(true),
|
"show-bar" => ShowBar(true),
|
||||||
"hide-bar" => ShowBar(false),
|
"hide-bar" => ShowBar(false),
|
||||||
"toggle-bar" => ToggleBar,
|
"toggle-bar" => ToggleBar,
|
||||||
|
"focus-prev" => FocusHistory(Timeline::Older),
|
||||||
|
"focus-next" => FocusHistory(Timeline::Newer),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use {
|
||||||
drm_device_match::DrmDeviceMatchParser,
|
drm_device_match::DrmDeviceMatchParser,
|
||||||
env::EnvParser,
|
env::EnvParser,
|
||||||
float::FloatParser,
|
float::FloatParser,
|
||||||
|
focus_history::FocusHistoryParser,
|
||||||
gfx_api::GfxApiParser,
|
gfx_api::GfxApiParser,
|
||||||
idle::IdleParser,
|
idle::IdleParser,
|
||||||
input::InputsParser,
|
input::InputsParser,
|
||||||
|
|
@ -133,6 +134,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
pointer_revert_key_str,
|
pointer_revert_key_str,
|
||||||
use_hardware_cursor,
|
use_hardware_cursor,
|
||||||
show_bar,
|
show_bar,
|
||||||
|
focus_history_val,
|
||||||
),
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
|
|
@ -181,6 +183,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
recover(opt(str("pointer-revert-key"))),
|
recover(opt(str("pointer-revert-key"))),
|
||||||
recover(opt(bol("use-hardware-cursor"))),
|
recover(opt(bol("use-hardware-cursor"))),
|
||||||
recover(opt(bol("show-bar"))),
|
recover(opt(bol("show-bar"))),
|
||||||
|
opt(val("focus-history")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
|
|
@ -458,6 +461,18 @@ impl Parser for ConfigParser<'_> {
|
||||||
None => log::warn!("Unknown keysym: {}", self.0.error3(value.span)),
|
None => log::warn!("Unknown keysym: {}", self.0.error3(value.span)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut focus_history = None;
|
||||||
|
if let Some(value) = focus_history_val {
|
||||||
|
match value.parse(&mut FocusHistoryParser(self.0)) {
|
||||||
|
Ok(v) => focus_history = Some(v),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!(
|
||||||
|
"Could not parse the focus-history settings: {}",
|
||||||
|
self.0.error(e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
keymap,
|
keymap,
|
||||||
repeat_rate,
|
repeat_rate,
|
||||||
|
|
@ -497,6 +512,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
pointer_revert_key,
|
pointer_revert_key,
|
||||||
use_hardware_cursor: use_hardware_cursor.despan(),
|
use_hardware_cursor: use_hardware_cursor.despan(),
|
||||||
show_bar: show_bar.despan(),
|
show_bar: show_bar.despan(),
|
||||||
|
focus_history,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
toml-config/src/config/parsers/focus_history.rs
Normal file
53
toml-config/src/config/parsers/focus_history.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
context::Context,
|
||||||
|
extractor::{Extractor, ExtractorError, bol, opt, recover},
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{DespanExt, Span, Spanned},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum FocusHistoryParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
Extract(#[from] ExtractorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FocusHistoryParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FocusHistory {
|
||||||
|
pub only_visible: Option<bool>,
|
||||||
|
pub same_workspace: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser for FocusHistoryParser<'_> {
|
||||||
|
type Value = FocusHistory;
|
||||||
|
type Error = FocusHistoryParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
|
let (only_visible, same_workspace) = ext.extract((
|
||||||
|
recover(opt(bol("only-visible"))),
|
||||||
|
recover(opt(bol("same-workspace"))),
|
||||||
|
))?;
|
||||||
|
Ok(FocusHistory {
|
||||||
|
only_visible: only_visible.despan(),
|
||||||
|
same_workspace: same_workspace.despan(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -156,6 +156,10 @@ impl Action {
|
||||||
SimpleCommand::KillClient => client_action!(c, c.kill()),
|
SimpleCommand::KillClient => client_action!(c, c.kill()),
|
||||||
SimpleCommand::ShowBar(show) => B::new(move || set_show_bar(show)),
|
SimpleCommand::ShowBar(show) => B::new(move || set_show_bar(show)),
|
||||||
SimpleCommand::ToggleBar => B::new(toggle_show_bar),
|
SimpleCommand::ToggleBar => B::new(toggle_show_bar),
|
||||||
|
SimpleCommand::FocusHistory(timeline) => {
|
||||||
|
let persistent = state.persistent.clone();
|
||||||
|
B::new(move || persistent.seat.focus_history(timeline))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||||
|
|
@ -1252,6 +1256,14 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
||||||
if let Some(v) = config.show_bar {
|
if let Some(v) = config.show_bar {
|
||||||
set_show_bar(v);
|
set_show_bar(v);
|
||||||
}
|
}
|
||||||
|
if let Some(v) = config.focus_history {
|
||||||
|
if let Some(v) = v.only_visible {
|
||||||
|
persistent.seat.focus_history_set_only_visible(v);
|
||||||
|
}
|
||||||
|
if let Some(v) = v.same_workspace {
|
||||||
|
persistent.seat.focus_history_set_same_workspace(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_command(exec: &Exec) -> Command {
|
fn create_command(exec: &Exec) -> Command {
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,10 @@
|
||||||
"show-bar": {
|
"show-bar": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n"
|
"description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n"
|
||||||
|
},
|
||||||
|
"focus-history": {
|
||||||
|
"description": "Configures the focus-history settings.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n",
|
||||||
|
"$ref": "#/$defs/FocusHistory"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
@ -1094,6 +1098,21 @@
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
},
|
},
|
||||||
|
"FocusHistory": {
|
||||||
|
"description": "Describes settings of the focus history.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"only-visible": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Sets whether the focus history only moves to windows that are already visible.\n\nIf this is false, then the window will be made visible before focusing it.\n\nThe default is `false`.\n"
|
||||||
|
},
|
||||||
|
"same-workspace": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Sets whether the focus history only moves to windows on the same workspace.\n\nThe default is `false`.\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
"Format": {
|
"Format": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A graphics format.\n\nThese formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
|
"description": "A graphics format.\n\nThese formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
|
||||||
|
|
@ -1593,7 +1612,9 @@
|
||||||
"kill-client",
|
"kill-client",
|
||||||
"show-bar",
|
"show-bar",
|
||||||
"hide-bar",
|
"hide-bar",
|
||||||
"toggle-bar"
|
"toggle-bar",
|
||||||
|
"focus-prev",
|
||||||
|
"focus-next"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Status": {
|
"Status": {
|
||||||
|
|
|
||||||
|
|
@ -1802,6 +1802,20 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a boolean.
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
- `focus-history` (optional):
|
||||||
|
|
||||||
|
Configures the focus-history settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[focus-history]
|
||||||
|
only-visible: true
|
||||||
|
same-workspace: true
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of this field should be a [FocusHistory](#types-FocusHistory).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Connector"></a>
|
<a name="types-Connector"></a>
|
||||||
### `Connector`
|
### `Connector`
|
||||||
|
|
@ -2232,6 +2246,42 @@ The table has the following fields:
|
||||||
The value of this field should be a boolean.
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-FocusHistory"></a>
|
||||||
|
### `FocusHistory`
|
||||||
|
|
||||||
|
Describes settings of the focus history.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[focus-history]
|
||||||
|
only-visible: true
|
||||||
|
same-workspace: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `only-visible` (optional):
|
||||||
|
|
||||||
|
Sets whether the focus history only moves to windows that are already visible.
|
||||||
|
|
||||||
|
If this is false, then the window will be made visible before focusing it.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
|
||||||
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
- `same-workspace` (optional):
|
||||||
|
|
||||||
|
Sets whether the focus history only moves to windows on the same workspace.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
|
||||||
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Format"></a>
|
<a name="types-Format"></a>
|
||||||
### `Format`
|
### `Format`
|
||||||
|
|
||||||
|
|
@ -3617,6 +3667,14 @@ The string should have one of the following values:
|
||||||
|
|
||||||
Toggles the built-in bar.
|
Toggles the built-in bar.
|
||||||
|
|
||||||
|
- `focus-prev`:
|
||||||
|
|
||||||
|
Focuses the previous window in the focus history.
|
||||||
|
|
||||||
|
- `focus-next`:
|
||||||
|
|
||||||
|
Focuses the next window in the focus history.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Status"></a>
|
<a name="types-Status"></a>
|
||||||
|
|
|
||||||
|
|
@ -860,6 +860,10 @@ SimpleActionName:
|
||||||
description: Hides the built-in bar.
|
description: Hides the built-in bar.
|
||||||
- value: toggle-bar
|
- value: toggle-bar
|
||||||
description: Toggles the built-in bar.
|
description: Toggles the built-in bar.
|
||||||
|
- value: focus-prev
|
||||||
|
description: Focuses the previous window in the focus history.
|
||||||
|
- value: focus-next
|
||||||
|
description: Focuses the next window in the focus history.
|
||||||
|
|
||||||
|
|
||||||
Color:
|
Color:
|
||||||
|
|
@ -2627,6 +2631,19 @@ Config:
|
||||||
Configures whether the built-in bar is shown.
|
Configures whether the built-in bar is shown.
|
||||||
|
|
||||||
The default is `true`.
|
The default is `true`.
|
||||||
|
focus-history:
|
||||||
|
ref: FocusHistory
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Configures the focus-history settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[focus-history]
|
||||||
|
only-visible: true
|
||||||
|
same-workspace: true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Idle:
|
Idle:
|
||||||
|
|
@ -3716,3 +3733,34 @@ ContentTypeMask:
|
||||||
description: An array of masks that are OR'd.
|
description: An array of masks that are OR'd.
|
||||||
items:
|
items:
|
||||||
ref: ContentTypeMask
|
ref: ContentTypeMask
|
||||||
|
|
||||||
|
|
||||||
|
FocusHistory:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
Describes settings of the focus history.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[focus-history]
|
||||||
|
only-visible: true
|
||||||
|
same-workspace: true
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
only-visible:
|
||||||
|
description: |
|
||||||
|
Sets whether the focus history only moves to windows that are already visible.
|
||||||
|
|
||||||
|
If this is false, then the window will be made visible before focusing it.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
kind: boolean
|
||||||
|
required: false
|
||||||
|
same-workspace:
|
||||||
|
description: |
|
||||||
|
Sets whether the focus history only moves to windows on the same workspace.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
kind: boolean
|
||||||
|
required: false
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue