control-center: add in-process control center
This commit is contained in:
parent
008e8a671a
commit
186d5b694b
28 changed files with 859 additions and 14 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
|
@ -407,6 +407,24 @@ dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_tiles"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ef184e589f0a80560bd3b63017634642d1ba112a8a8d9b29341f7cafd04601f"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"egui",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
|
|
@ -681,6 +699,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1697e6b71679da96d5c41bb9035116141baadbf59a60625fd66cb3c9584e7b0"
|
checksum = "f1697e6b71679da96d5c41bb9035116141baadbf59a60625fd66cb3c9584e7b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
@ -722,6 +749,7 @@ dependencies = [
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"dirs",
|
"dirs",
|
||||||
"egui",
|
"egui",
|
||||||
|
"egui_tiles",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gpu-alloc",
|
"gpu-alloc",
|
||||||
"gpu-alloc-types",
|
"gpu-alloc-types",
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ with_builtin_macros = "0.1.0"
|
||||||
blake3 = "1.8.2"
|
blake3 = "1.8.2"
|
||||||
run-on-drop = "1.0.0"
|
run-on-drop = "1.0.0"
|
||||||
egui = { version = "0.33.3", default-features = false }
|
egui = { version = "0.33.3", default-features = false }
|
||||||
|
egui_tiles = { version = "0.14.1", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -1046,6 +1046,10 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
|
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_control_center(&self) {
|
||||||
|
self.send(&ClientMessage::OpenControlCenter);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||||
self.send(&ClientMessage::SetWorkspaceDisplayOrder { order });
|
self.send(&ClientMessage::SetWorkspaceDisplayOrder { order });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -845,6 +845,7 @@ pub enum ClientMessage<'a> {
|
||||||
proportional: Option<Vec<&'a str>>,
|
proportional: Option<Vec<&'a str>>,
|
||||||
monospace: Option<Vec<&'a str>>,
|
monospace: Option<Vec<&'a str>>,
|
||||||
},
|
},
|
||||||
|
OpenControlCenter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -380,3 +380,8 @@ pub fn on_unload(f: impl FnOnce() + 'static) {
|
||||||
pub fn set_middle_click_paste_enabled(enabled: bool) {
|
pub fn set_middle_click_paste_enabled(enabled: bool) {
|
||||||
get!().set_middle_click_paste_enabled(enabled);
|
get!().set_middle_click_paste_enabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the control center.
|
||||||
|
pub fn open_control_center() {
|
||||||
|
get!().open_control_center();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod clients;
|
mod clients;
|
||||||
mod color;
|
mod color;
|
||||||
mod color_management;
|
mod color_management;
|
||||||
|
mod control_center;
|
||||||
mod damage_tracking;
|
mod damage_tracking;
|
||||||
mod duration;
|
mod duration;
|
||||||
mod generate;
|
mod generate;
|
||||||
|
|
@ -97,6 +98,8 @@ pub enum Cmd {
|
||||||
Clients(ClientsArgs),
|
Clients(ClientsArgs),
|
||||||
/// Inspect the surface tree.
|
/// Inspect the surface tree.
|
||||||
Tree(TreeArgs),
|
Tree(TreeArgs),
|
||||||
|
/// Opens the control center.
|
||||||
|
ControlCenter,
|
||||||
/// Prints the Jay version and exits.
|
/// Prints the Jay version and exits.
|
||||||
Version,
|
Version,
|
||||||
/// Prints the Jay PID and exits.
|
/// Prints the Jay PID and exits.
|
||||||
|
|
@ -243,5 +246,6 @@ pub fn main() {
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
Cmd::RunTests => crate::it::run_tests(),
|
Cmd::RunTests => crate::it::run_tests(),
|
||||||
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
||||||
|
Cmd::ControlCenter => control_center::main(cli.global),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
src/cli/control_center.rs
Normal file
32
src/cli/control_center.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::GlobalArgs,
|
||||||
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
|
wire::{jay_compositor, jay_open_control_center_request},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main(global: GlobalArgs) {
|
||||||
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
|
let cc = ControlCenter { tc: tc.clone() };
|
||||||
|
cc.run().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ControlCenter {
|
||||||
|
tc: Rc<ToolClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenter {
|
||||||
|
async fn run(self) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
let comp = tc.jay_compositor().await;
|
||||||
|
let id = tc.id();
|
||||||
|
tc.send(jay_compositor::OpenControlCenter { self_id: comp, id });
|
||||||
|
jay_open_control_center_request::Failed::handle(&tc, id, (), |_, ev| {
|
||||||
|
fatal!("Could not open the control center: {}", ev.msg);
|
||||||
|
});
|
||||||
|
tc.round_trip().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
clientmem::{self, ClientMemError},
|
clientmem::{self, ClientMemError},
|
||||||
cmm::{cmm_manager::ColorManager, cmm_primaries::Primaries},
|
cmm::{cmm_manager::ColorManager, cmm_primaries::Primaries},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
|
control_center::redraw_control_centers,
|
||||||
copy_device::CopyDeviceRegistry,
|
copy_device::CopyDeviceRegistry,
|
||||||
cpu_worker::{CpuWorker, CpuWorkerError},
|
cpu_worker::{CpuWorker, CpuWorkerError},
|
||||||
criteria::{
|
criteria::{
|
||||||
|
|
@ -393,6 +394,7 @@ fn start_compositor2(
|
||||||
lazy_event_sources: Default::default(),
|
lazy_event_sources: Default::default(),
|
||||||
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
||||||
egg_state: Default::default(),
|
egg_state: Default::default(),
|
||||||
|
control_centers: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -417,6 +419,7 @@ fn start_compositor2(
|
||||||
let _compositor = engine.spawn("compositor", start_compositor3(state.clone(), test_future));
|
let _compositor = engine.spawn("compositor", start_compositor3(state.clone(), test_future));
|
||||||
ring.run()?;
|
ring.run()?;
|
||||||
state.clear();
|
state.clear();
|
||||||
|
engine.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -597,6 +600,10 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||||
"lazy event sources",
|
"lazy event sources",
|
||||||
handle_lazy_event_sources(state.clone()),
|
handle_lazy_event_sources(state.clone()),
|
||||||
),
|
),
|
||||||
|
eng.spawn(
|
||||||
|
"redraw control centers",
|
||||||
|
redraw_control_centers(state.clone()),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1811,6 +1811,12 @@ impl ConfigProxyHandler {
|
||||||
self.state.set_egui_fonts(proportional, monospace);
|
self.state.set_egui_fonts(proportional, monospace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_open_control_center(&self) {
|
||||||
|
if let Err(e) = self.state.open_control_center() {
|
||||||
|
log::error!("Could not open control center: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_log_level(&self, level: ConfigLogLevel) {
|
fn handle_set_log_level(&self, level: ConfigLogLevel) {
|
||||||
self.state.set_log_level(level.into());
|
self.state.set_log_level(level.into());
|
||||||
}
|
}
|
||||||
|
|
@ -3319,6 +3325,7 @@ impl ConfigProxyHandler {
|
||||||
proportional,
|
proportional,
|
||||||
monospace,
|
monospace,
|
||||||
} => self.handle_set_egui_fonts(proportional, monospace),
|
} => self.handle_set_egui_fonts(proportional, monospace),
|
||||||
|
ClientMessage::OpenControlCenter => self.handle_open_control_center(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
606
src/control_center.rs
Normal file
606
src/control_center.rs
Normal file
|
|
@ -0,0 +1,606 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
egui_adapter::egui_platform::{
|
||||||
|
EggError, EggWindow, EggWindowOwner,
|
||||||
|
icons::{ICON_CLOSE, ICON_DRAG_INDICATOR, ICON_INFO},
|
||||||
|
},
|
||||||
|
macros::Bitflag,
|
||||||
|
state::State,
|
||||||
|
utils::{
|
||||||
|
asyncevent::AsyncEvent, copyhashmap::CopyHashMap, numcell::NumCell,
|
||||||
|
static_text::StaticText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
egui::{
|
||||||
|
Align, CentralPanel, Checkbox, Color32, ComboBox, Context, CursorIcon, DragValue, Frame,
|
||||||
|
Grid, InnerResponse, Label, Layout, Response, Rgba, RichText, ScrollArea, Sense, SidePanel,
|
||||||
|
Stroke, TextBuffer, TextEdit, Ui, UiBuilder, Visuals, Widget, WidgetText, emath::Numeric,
|
||||||
|
vec2,
|
||||||
|
},
|
||||||
|
egui_tiles::{ResizeState, TabState, Tile, TileId, Tiles, Tree},
|
||||||
|
linearize::{Linearize, LinearizeExt},
|
||||||
|
std::{
|
||||||
|
cell::RefCell,
|
||||||
|
hash::Hash,
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut, RangeInclusive},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod cc_sidebar;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ControlCenterError {
|
||||||
|
#[error("Could not get the egg context")]
|
||||||
|
GetEggContext(#[source] EggError),
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(ControlCenterIds, ControlCenterId, u64);
|
||||||
|
|
||||||
|
pub async fn redraw_control_centers(state: Rc<State>) {
|
||||||
|
let cc = &state.control_centers;
|
||||||
|
loop {
|
||||||
|
cc.redraw.triggered().await;
|
||||||
|
let interests = cc.change.take();
|
||||||
|
for cc in cc.control_centers.lock().values() {
|
||||||
|
if cc.inner.interests.interests.get().intersects(interests) {
|
||||||
|
cc.inner.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ControlCenters {
|
||||||
|
ids: ControlCenterIds,
|
||||||
|
change: NumCell<ControlCenterInterest>,
|
||||||
|
redraw: AsyncEvent,
|
||||||
|
control_centers: CopyHashMap<ControlCenterId, Rc<ControlCenter>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
ControlCenterInterest: u32;
|
||||||
|
_UNUSED,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlCenter {
|
||||||
|
inner: Rc<ControlCenterInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(PaneIds, PaneId, u64);
|
||||||
|
|
||||||
|
struct ControlCenterInner {
|
||||||
|
id: ControlCenterId,
|
||||||
|
state: Rc<State>,
|
||||||
|
tree: RefCell<Settings>,
|
||||||
|
window: Rc<EggWindow>,
|
||||||
|
pane_ids: PaneIds,
|
||||||
|
interests: Rc<Interests>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Interests {
|
||||||
|
interests: NumCell<ControlCenterInterest>,
|
||||||
|
interests_array: [NumCell<u64>; <ControlCenterInterest as Bitflag>::Type::BITS as usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
tree: Tree<Pane>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pane {
|
||||||
|
id: PaneId,
|
||||||
|
ps: PaneState,
|
||||||
|
own_interests: ControlCenterInterest,
|
||||||
|
cc_interests: Rc<Interests>,
|
||||||
|
ty: PaneType,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaneState {
|
||||||
|
errors: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PaneType {}
|
||||||
|
|
||||||
|
struct CcBehavior<'a> {
|
||||||
|
#[expect(dead_code)]
|
||||||
|
cc: &'a Rc<ControlCenterInner>,
|
||||||
|
close: Option<TileId>,
|
||||||
|
open: Option<PaneType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenters {
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.control_centers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pane {
|
||||||
|
fn title(&self, _res: &mut String) {
|
||||||
|
match self.ty {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, _behavior: &mut CcBehavior<'_>, _ui: &mut Ui) {
|
||||||
|
match self.ty {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneType {
|
||||||
|
fn interest(&self) -> ControlCenterInterest {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl egui_tiles::Behavior<Pane> for CcBehavior<'_> {
|
||||||
|
fn pane_ui(&mut self, ui: &mut Ui, tile_id: TileId, pane: &mut Pane) -> egui_tiles::UiResponse {
|
||||||
|
let mut drag = false;
|
||||||
|
Frame::central_panel(ui.style()).show(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
drag = ui
|
||||||
|
.add(icon_label(ICON_DRAG_INDICATOR).sense(Sense::drag()))
|
||||||
|
.total_drag_delta()
|
||||||
|
.map(|d| d.length() >= 5.0)
|
||||||
|
.unwrap_or(false);
|
||||||
|
let mut title = String::new();
|
||||||
|
pane.title(&mut title);
|
||||||
|
if ui
|
||||||
|
.add(icon_label(&title).sense(Sense::click()))
|
||||||
|
.middle_clicked()
|
||||||
|
{
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add(icon_label(ICON_CLOSE).sense(Sense::click()))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
show_errors(ui, &mut pane.ps);
|
||||||
|
ui.scope_builder(UiBuilder::new().id(("pane", pane.id)), |ui| {
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.allocate_space(vec2(ui.available_width(), 0.0));
|
||||||
|
pane.show(self, ui);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if drag {
|
||||||
|
egui_tiles::UiResponse::DragStarted
|
||||||
|
} else {
|
||||||
|
egui_tiles::UiResponse::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_title_for_pane(&mut self, _pane: &Pane) -> WidgetText {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_hover_cursor_icon(&self) -> CursorIcon {
|
||||||
|
CursorIcon::Default
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_title_for_tile(&mut self, tiles: &Tiles<Pane>, tile_id: TileId) -> WidgetText {
|
||||||
|
fn add_title(tiles: &Tiles<Pane>, res: &mut String, first: &mut bool, tile_id: TileId) {
|
||||||
|
if !mem::take(first) {
|
||||||
|
res.push_str("/");
|
||||||
|
}
|
||||||
|
let Some(tile) = tiles.get(tile_id) else {
|
||||||
|
res.push_str("MISSING TILE");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match tile {
|
||||||
|
Tile::Pane(p) => p.title(res),
|
||||||
|
Tile::Container(c) => {
|
||||||
|
let mut first = true;
|
||||||
|
for &tile_id in c.children() {
|
||||||
|
add_title(tiles, res, &mut first, tile_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut res = String::new();
|
||||||
|
let mut first = true;
|
||||||
|
add_title(tiles, &mut res, &mut first, tile_id);
|
||||||
|
res.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_tab_button(
|
||||||
|
&mut self,
|
||||||
|
_tiles: &Tiles<Pane>,
|
||||||
|
tile_id: TileId,
|
||||||
|
button_response: Response,
|
||||||
|
) -> Response {
|
||||||
|
if button_response.middle_clicked() {
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
button_response
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize_stroke(&self, style: &egui::Style, resize_state: ResizeState) -> Stroke {
|
||||||
|
match resize_state {
|
||||||
|
ResizeState::Idle => style.visuals.widgets.noninteractive.bg_stroke,
|
||||||
|
ResizeState::Hovering => style.visuals.widgets.hovered.fg_stroke,
|
||||||
|
ResizeState::Dragging => style.visuals.widgets.active.fg_stroke,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_bar_color(&self, visuals: &Visuals) -> Color32 {
|
||||||
|
(Rgba::from(visuals.panel_fill) * Rgba::from_gray(0.8)).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_bg_color(
|
||||||
|
&self,
|
||||||
|
visuals: &Visuals,
|
||||||
|
_tiles: &Tiles<Pane>,
|
||||||
|
_tile_id: TileId,
|
||||||
|
state: &TabState,
|
||||||
|
) -> Color32 {
|
||||||
|
match state.active {
|
||||||
|
true => visuals.panel_fill,
|
||||||
|
false => self.tab_bar_color(visuals),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EggWindowOwner for ControlCenterInner {
|
||||||
|
fn close(&self) {
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(self: Rc<Self>, ctx: &Context) {
|
||||||
|
let settings = &mut *self.tree.borrow_mut();
|
||||||
|
SidePanel::left("sidebar").show(ctx, |ui| self.show_sidebar(&mut settings.tree, ui));
|
||||||
|
CentralPanel::default()
|
||||||
|
.frame(
|
||||||
|
Frame::central_panel(&ctx.style())
|
||||||
|
.outer_margin(0.0)
|
||||||
|
.inner_margin(0.0),
|
||||||
|
)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
let tree = &mut settings.tree;
|
||||||
|
let mut behavior = CcBehavior {
|
||||||
|
cc: &self,
|
||||||
|
close: Default::default(),
|
||||||
|
open: Default::default(),
|
||||||
|
};
|
||||||
|
tree.ui(&mut behavior, ui);
|
||||||
|
if let Some(close) = behavior.close {
|
||||||
|
tree.set_visible(close, false);
|
||||||
|
tree.remove_recursively(close);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
if let Some(ty) = behavior.open {
|
||||||
|
self.open(tree, ty);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn open_control_center(self: &Rc<Self>) -> Result<Rc<ControlCenter>, ControlCenterError> {
|
||||||
|
let ctx = self
|
||||||
|
.get_egg_context()
|
||||||
|
.map_err(ControlCenterError::GetEggContext)?;
|
||||||
|
let window = ctx.create_window("Control Center");
|
||||||
|
let cc = Rc::new(ControlCenter {
|
||||||
|
inner: Rc::new(ControlCenterInner {
|
||||||
|
id: self.control_centers.ids.next(),
|
||||||
|
window,
|
||||||
|
state: self.clone(),
|
||||||
|
tree: RefCell::new(Settings {
|
||||||
|
tree: Tree::new_tabs("abcd", vec![]),
|
||||||
|
}),
|
||||||
|
pane_ids: Default::default(),
|
||||||
|
interests: Default::default(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
cc.inner.window.set_owner(Some(cc.inner.clone()));
|
||||||
|
self.control_centers
|
||||||
|
.control_centers
|
||||||
|
.set(cc.inner.id, cc.clone());
|
||||||
|
Ok(cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn trigger_cci(&self, cci: ControlCenterInterest) {
|
||||||
|
self.control_centers.change.or_assign(cci);
|
||||||
|
self.control_centers.redraw.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
fn close(&self) {
|
||||||
|
self.window.set_owner(None);
|
||||||
|
self.tree.borrow_mut().tree = Tree::empty("");
|
||||||
|
self.state.control_centers.control_centers.remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ControlCenter {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.inner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
fn create_pane(&self, ty: PaneType) -> Pane {
|
||||||
|
let pane = Pane {
|
||||||
|
id: self.pane_ids.next(),
|
||||||
|
ps: PaneState {
|
||||||
|
errors: Default::default(),
|
||||||
|
},
|
||||||
|
own_interests: ty.interest(),
|
||||||
|
cc_interests: self.interests.clone(),
|
||||||
|
ty,
|
||||||
|
};
|
||||||
|
let own = pane.own_interests;
|
||||||
|
for (idx, v) in pane.cc_interests.interests_array.iter().enumerate() {
|
||||||
|
let interest = ControlCenterInterest(1 << idx);
|
||||||
|
if own.intersects(interest) && v.fetch_add(1) == 0 {
|
||||||
|
pane.cc_interests.interests.or_assign(interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self, tree: &mut Tree<Pane>, ty: PaneType) {
|
||||||
|
let _ = tree;
|
||||||
|
#[expect(unused_variables, unreachable_code)]
|
||||||
|
let pane = self.create_pane(ty);
|
||||||
|
let id = tree.tiles.insert_pane(pane);
|
||||||
|
if let Some(root) = tree.root
|
||||||
|
&& let Some(tile) = tree.tiles.get_mut(root)
|
||||||
|
{
|
||||||
|
match tile {
|
||||||
|
Tile::Container(c) => {
|
||||||
|
c.add_child(id);
|
||||||
|
}
|
||||||
|
Tile::Pane(_) => {
|
||||||
|
let root = tree.tiles.insert_tab_tile(vec![root, id]);
|
||||||
|
tree.root = Some(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tree.root = Some(id);
|
||||||
|
}
|
||||||
|
tree.make_active(|t, _| t == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Pane {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let own = self.own_interests;
|
||||||
|
for (idx, v) in self.cc_interests.interests_array.iter().enumerate() {
|
||||||
|
let interest = ControlCenterInterest(1 << idx);
|
||||||
|
if own.intersects(interest) && v.fetch_sub(1) == 1 {
|
||||||
|
self.cc_interests.interests.and_assign(!interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icon_label(icon: &str) -> Label {
|
||||||
|
Label::new(icon).selectable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn grid_label(ui: &mut Ui, label: &str) {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
|
ui.label(label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grid_label_ui<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::Center), add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn tip(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
|
||||||
|
icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn text_edit(ui: &mut Ui, v: &mut dyn TextBuffer) -> Response {
|
||||||
|
TextEdit::singleline(v)
|
||||||
|
.clip_text(false)
|
||||||
|
.min_size(vec2(200.0, 0.0))
|
||||||
|
.ui(ui)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_errors(ui: &mut Ui, pane: &mut PaneState) {
|
||||||
|
if pane.errors.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut to_remove = None;
|
||||||
|
for (idx, e) in pane.errors.iter().enumerate() {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
Frame::new().inner_margin(5.0).show(ui, |ui| {
|
||||||
|
if ui.button(ICON_CLOSE).clicked() {
|
||||||
|
to_remove = Some(idx);
|
||||||
|
}
|
||||||
|
ui.label(
|
||||||
|
RichText::new("Error:")
|
||||||
|
.strong()
|
||||||
|
.color(ui.style().visuals.error_fg_color),
|
||||||
|
);
|
||||||
|
ui.add(Label::new(e).wrap());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(idx) = to_remove {
|
||||||
|
pane.errors.remove(idx);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn grid<R>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
id_salt: impl Hash,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> InnerResponse<R> {
|
||||||
|
let mut spacing = ui.spacing().item_spacing;
|
||||||
|
spacing.x *= 3.0;
|
||||||
|
Grid::new(id_salt).spacing(spacing).show(ui, add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn row<R>(ui: &mut Ui, name: &str, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
||||||
|
row_ui(ui, name, |_| (), add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn row_ui<R, S>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> S,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> R {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label_ui(ui, |ui| {
|
||||||
|
ui.label(name);
|
||||||
|
label(ui);
|
||||||
|
});
|
||||||
|
add_contents(ui)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn bool(ui: &mut Ui, name: &str, old: bool, set: impl FnOnce(bool)) {
|
||||||
|
bool_ui(ui, name, |_| (), old, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_ui<R>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: bool,
|
||||||
|
set: impl FnOnce(bool),
|
||||||
|
) {
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
if Checkbox::without_text(&mut v).ui(ui).changed() {
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn read_only_bool(ui: &mut Ui, name: &str, old: bool) {
|
||||||
|
read_only_bool_ui(ui, name, |_| (), old);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_only_bool_ui<R>(ui: &mut Ui, name: &str, label: impl FnOnce(&mut Ui) -> R, mut v: bool) {
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
ui.add_enabled_ui(false, |ui| Checkbox::without_text(&mut v).ui(ui));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn combo_box<T>(ui: &mut Ui, name: &str, old: T, set: impl FnOnce(T))
|
||||||
|
where
|
||||||
|
T: StaticText + Linearize + PartialEq + Copy,
|
||||||
|
{
|
||||||
|
combo_box_ui(ui, name, |_| (), old, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combo_box_ui<R, T>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: T,
|
||||||
|
set: impl FnOnce(T),
|
||||||
|
) where
|
||||||
|
T: StaticText + Linearize + PartialEq + Copy,
|
||||||
|
{
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
let old = v;
|
||||||
|
ComboBox::from_id_salt(name)
|
||||||
|
.selected_text(v.text())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for s in T::variants() {
|
||||||
|
ui.selectable_value(&mut v, s, s.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if old != v {
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn drag_value<N>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
old: N,
|
||||||
|
range: RangeInclusive<N>,
|
||||||
|
speed: f64,
|
||||||
|
set: impl FnOnce(N),
|
||||||
|
) where
|
||||||
|
N: Numeric,
|
||||||
|
{
|
||||||
|
drag_value_ui(ui, name, |_| (), old, range, speed, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_value_ui<R, N>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: N,
|
||||||
|
range: RangeInclusive<N>,
|
||||||
|
speed: f64,
|
||||||
|
set: impl FnOnce(N),
|
||||||
|
) where
|
||||||
|
N: Numeric,
|
||||||
|
{
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
if DragValue::new(&mut v)
|
||||||
|
.range(range)
|
||||||
|
.speed(speed)
|
||||||
|
.ui(ui)
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn label(ui: &mut Ui, name: &str, text: impl Into<WidgetText>) {
|
||||||
|
row(ui, name, |ui| ui.label(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
trait GridExt {
|
||||||
|
fn row(&mut self) -> impl DerefMut<Target = Ui>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridExt for Ui {
|
||||||
|
fn row(&mut self) -> impl DerefMut<Target = Ui> {
|
||||||
|
GridRow { ui: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridRow<'a> {
|
||||||
|
ui: &'a mut Ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GridRow<'_> {
|
||||||
|
type Target = Ui;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.ui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for GridRow<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut *self.ui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GridRow<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.end_row();
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/control_center/cc_sidebar.rs
Normal file
48
src/control_center/cc_sidebar.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use {
|
||||||
|
crate::control_center::{ControlCenterInner, Pane},
|
||||||
|
egui::{Align, Layout, ScrollArea, Ui, ViewportCommand},
|
||||||
|
egui_tiles::Tree,
|
||||||
|
linearize::{Linearize, LinearizeExt},
|
||||||
|
std::{rc::Rc, sync::LazyLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Linearize)]
|
||||||
|
enum PaneName {}
|
||||||
|
|
||||||
|
impl PaneName {
|
||||||
|
fn name(self) -> &'static str {
|
||||||
|
match self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TYPES: LazyLock<Vec<PaneName>> = LazyLock::new(|| {
|
||||||
|
let mut res: Vec<_> = PaneName::variants().collect();
|
||||||
|
res.sort_by_key(|t| t.name());
|
||||||
|
res
|
||||||
|
});
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn show_sidebar(self: &Rc<Self>, tree: &mut Tree<Pane>, ui: &mut Ui) {
|
||||||
|
ui.with_layout(
|
||||||
|
Layout::top_down(Align::Center).with_cross_justify(true),
|
||||||
|
|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
if ui.button("Close").clicked() {
|
||||||
|
ui.ctx().send_viewport_cmd(ViewportCommand::Close);
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
for &ty in &*TYPES {
|
||||||
|
if ui.button(ty.name()).clicked() {
|
||||||
|
let _ty = match ty {};
|
||||||
|
#[expect(unreachable_code)]
|
||||||
|
self.open(tree, _ty);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.add_space(3.0);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -112,11 +112,8 @@ pub enum EggError {
|
||||||
pub mod icons {
|
pub mod icons {
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub const ICON_ADD: &str = "\u{e145}";
|
pub const ICON_ADD: &str = "\u{e145}";
|
||||||
#[expect(dead_code)]
|
|
||||||
pub const ICON_CLOSE: &str = "\u{e5cd}";
|
pub const ICON_CLOSE: &str = "\u{e5cd}";
|
||||||
#[expect(dead_code)]
|
|
||||||
pub const ICON_DRAG_INDICATOR: &str = "\u{e945}";
|
pub const ICON_DRAG_INDICATOR: &str = "\u{e945}";
|
||||||
#[expect(dead_code)]
|
|
||||||
pub const ICON_INFO: &str = "\u{e88e}";
|
pub const ICON_INFO: &str = "\u{e88e}";
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub const ICON_OPEN_IN_NEW: &str = "\u{e89e}";
|
pub const ICON_OPEN_IN_NEW: &str = "\u{e89e}";
|
||||||
|
|
@ -361,7 +358,6 @@ impl EggState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_egg_context(self: &Rc<Self>) -> Result<Rc<EggContext>, EggError> {
|
pub fn get_egg_context(self: &Rc<Self>) -> Result<Rc<EggContext>, EggError> {
|
||||||
if let Some(ctx) = self.egg_state.ctx.get() {
|
if let Some(ctx) = self.egg_state.ctx.get() {
|
||||||
return Ok(ctx);
|
return Ok(ctx);
|
||||||
|
|
@ -467,7 +463,6 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EggContext {
|
impl EggContext {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn create_window(self: &Rc<Self>, title: &str) -> Rc<EggWindow> {
|
pub fn create_window(self: &Rc<Self>, title: &str) -> Rc<EggWindow> {
|
||||||
let i = &self.inner;
|
let i = &self.inner;
|
||||||
let wl_surface = i.wl_compositor.create_surface();
|
let wl_surface = i.wl_compositor.create_surface();
|
||||||
|
|
@ -628,12 +623,10 @@ impl EggSeatInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EggWindow {
|
impl EggWindow {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
self.inner.want_frame();
|
self.inner.want_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_owner(&self, owner: Option<Rc<dyn EggWindowOwner>>) {
|
pub fn set_owner(&self, owner: Option<Rc<dyn EggWindowOwner>>) {
|
||||||
self.inner.owner.set(owner);
|
self.inner.owner.set(owner);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub mod jay_ei_session_builder;
|
||||||
pub mod jay_idle;
|
pub mod jay_idle;
|
||||||
pub mod jay_input;
|
pub mod jay_input;
|
||||||
pub mod jay_log_file;
|
pub mod jay_log_file;
|
||||||
|
pub mod jay_open_control_center_request;
|
||||||
pub mod jay_output;
|
pub mod jay_output;
|
||||||
pub mod jay_pointer;
|
pub mod jay_pointer;
|
||||||
pub mod jay_popup_ext_manager_v1;
|
pub mod jay_popup_ext_manager_v1;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
jay_idle::JayIdle,
|
jay_idle::JayIdle,
|
||||||
jay_input::JayInput,
|
jay_input::JayInput,
|
||||||
jay_log_file::JayLogFile,
|
jay_log_file::JayLogFile,
|
||||||
|
jay_open_control_center_request::JayOpenControlCenterRequest,
|
||||||
jay_output::JayOutput,
|
jay_output::JayOutput,
|
||||||
jay_pointer::JayPointer,
|
jay_pointer::JayPointer,
|
||||||
jay_randr::JayRandr,
|
jay_randr::JayRandr,
|
||||||
|
|
@ -77,7 +78,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
|
||||||
|
|
||||||
impl Global for JayCompositorGlobal {
|
impl Global for JayCompositorGlobal {
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
27
|
28
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -541,6 +542,25 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_control_center(
|
||||||
|
&self,
|
||||||
|
req: OpenControlCenter,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let obj = Rc::new(JayOpenControlCenterRequest {
|
||||||
|
id: req.id,
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version: self.version,
|
||||||
|
});
|
||||||
|
track!(self.client, obj);
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
if let Err(e) = self.client.state.open_control_center() {
|
||||||
|
obj.send_failed(e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
53
src/ifs/jay_open_control_center_request.rs
Normal file
53
src/ifs/jay_open_control_center_request.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
utils::errorfmt::ErrorFmt,
|
||||||
|
wire::{JayOpenControlCenterRequestId, jay_open_control_center_request::*},
|
||||||
|
},
|
||||||
|
std::{error::Error, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct JayOpenControlCenterRequest {
|
||||||
|
pub id: JayOpenControlCenterRequestId,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayOpenControlCenterRequest {
|
||||||
|
pub fn send_failed(&self, err: impl Error) {
|
||||||
|
let msg = &ErrorFmt(err).to_string();
|
||||||
|
self.client.event(Failed {
|
||||||
|
self_id: self.id,
|
||||||
|
msg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayOpenControlCenterRequestRequestHandler for JayOpenControlCenterRequest {
|
||||||
|
type Error = JayOpenControlCenterRequestError;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = JayOpenControlCenterRequest;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for JayOpenControlCenterRequest {}
|
||||||
|
|
||||||
|
simple_add_obj!(JayOpenControlCenterRequest);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum JayOpenControlCenterRequestError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
}
|
||||||
|
efrom!(JayOpenControlCenterRequestError, ClientError);
|
||||||
|
|
@ -58,6 +58,7 @@ mod clientmem;
|
||||||
mod cmm;
|
mod cmm;
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod control_center;
|
||||||
mod copy_device;
|
mod copy_device;
|
||||||
mod cpu_worker;
|
mod cpu_worker;
|
||||||
mod criteria;
|
mod criteria;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use {
|
||||||
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
||||||
compositor::{LIBEI_SOCKET, LogLevel},
|
compositor::{LIBEI_SOCKET, LogLevel},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
|
control_center::ControlCenters,
|
||||||
copy_device::CopyDeviceRegistry,
|
copy_device::CopyDeviceRegistry,
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
|
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
|
||||||
|
|
@ -297,6 +298,7 @@ pub struct State {
|
||||||
pub lazy_event_sources: Rc<LazyEventSources>,
|
pub lazy_event_sources: Rc<LazyEventSources>,
|
||||||
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
|
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
|
||||||
pub egg_state: EggState,
|
pub egg_state: EggState,
|
||||||
|
pub control_centers: ControlCenters,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -1162,6 +1164,7 @@ impl State {
|
||||||
self.lazy_event_sources.clear();
|
self.lazy_event_sources.clear();
|
||||||
self.bo_drop_queue.kill();
|
self.bo_drop_queue.kill();
|
||||||
self.egg_state.clear();
|
self.egg_state.clear();
|
||||||
|
self.control_centers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(27),
|
version: s.jay_compositor.1.min(28),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ impl<T> NumCell<T> {
|
||||||
{
|
{
|
||||||
!self.is_zero()
|
!self.is_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn take(&self) -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.t.replace(T::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BitOr<Output = T> + Copy> BitOr<T> for &'_ NumCell<T> {
|
impl<T: BitOr<Output = T> + Copy> BitOr<T> for &'_ NumCell<T> {
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ pub enum SimpleCommand {
|
||||||
ToggleSimpleImEnabled,
|
ToggleSimpleImEnabled,
|
||||||
ReloadSimpleIm,
|
ReloadSimpleIm,
|
||||||
EnableUnicodeInput,
|
EnableUnicodeInput,
|
||||||
|
OpenControlCenter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ impl ActionParser<'_> {
|
||||||
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
||||||
"reload-simple-im" => ReloadSimpleIm,
|
"reload-simple-im" => ReloadSimpleIm,
|
||||||
"enable-unicode-input" => EnableUnicodeInput,
|
"enable-unicode-input" => EnableUnicodeInput,
|
||||||
|
"open-control-center" => OpenControlCenter,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ alt-m = "toggle-mono"
|
||||||
alt-u = "toggle-fullscreen"
|
alt-u = "toggle-fullscreen"
|
||||||
|
|
||||||
alt-f = "focus-parent"
|
alt-f = "focus-parent"
|
||||||
|
alt-c = "open-control-center"
|
||||||
alt-shift-c = "close"
|
alt-shift-c = "close"
|
||||||
alt-shift-f = "toggle-floating"
|
alt-shift-f = "toggle-floating"
|
||||||
Super_L = { type = "exec", exec = "alacritty" }
|
Super_L = { type = "exec", exec = "alacritty" }
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,11 @@ use {
|
||||||
is_reload,
|
is_reload,
|
||||||
keyboard::Keymap,
|
keyboard::Keymap,
|
||||||
logging::set_log_level,
|
logging::set_log_level,
|
||||||
on_devices_enumerated, on_idle, on_unload, quit, reload, set_color_management_enabled,
|
on_devices_enumerated, on_idle, on_unload, open_control_center, quit, reload,
|
||||||
set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen,
|
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
||||||
set_idle, set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar,
|
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
||||||
set_show_float_pin_icon, set_show_titles, set_ui_drag_enabled, set_ui_drag_threshold,
|
set_middle_click_paste_enabled, set_show_bar, set_show_float_pin_icon, set_show_titles,
|
||||||
|
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
tasks::{self, JoinHandle},
|
tasks::{self, JoinHandle},
|
||||||
|
|
@ -245,6 +246,7 @@ impl Action {
|
||||||
let persistent = state.persistent.clone();
|
let persistent = state.persistent.clone();
|
||||||
b.new(move || persistent.seat.enable_unicode_input())
|
b.new(move || persistent.seat.enable_unicode_input())
|
||||||
}
|
}
|
||||||
|
SimpleCommand::OpenControlCenter => b.new(open_control_center),
|
||||||
},
|
},
|
||||||
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();
|
||||||
|
|
|
||||||
|
|
@ -1928,7 +1928,8 @@
|
||||||
"disable-simple-im",
|
"disable-simple-im",
|
||||||
"toggle-simple-im-enabled",
|
"toggle-simple-im-enabled",
|
||||||
"reload-simple-im",
|
"reload-simple-im",
|
||||||
"enable-unicode-input"
|
"enable-unicode-input",
|
||||||
|
"open-control-center"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"SimpleIm": {
|
"SimpleIm": {
|
||||||
|
|
|
||||||
|
|
@ -4427,6 +4427,10 @@ The string should have one of the following values:
|
||||||
|
|
||||||
This has no effect if the simple IM is not currently active.
|
This has no effect if the simple IM is not currently active.
|
||||||
|
|
||||||
|
- `open-control-center`:
|
||||||
|
|
||||||
|
Opens the control center.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="types-SimpleIm"></a>
|
<a name="types-SimpleIm"></a>
|
||||||
|
|
|
||||||
|
|
@ -1089,6 +1089,8 @@ SimpleActionName:
|
||||||
Enables Unicode input in the simple, XCompose based input method.
|
Enables Unicode input in the simple, XCompose based input method.
|
||||||
|
|
||||||
This has no effect if the simple IM is not currently active.
|
This has no effect if the simple IM is not currently active.
|
||||||
|
- value: open-control-center
|
||||||
|
description: Opens the control center.
|
||||||
|
|
||||||
|
|
||||||
Color:
|
Color:
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,10 @@ request get_pid (since = 27) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request open_control_center (since = 28) {
|
||||||
|
id: id(jay_open_control_center_request),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
7
wire/jay_open_control_center_request.txt
Normal file
7
wire/jay_open_control_center_request.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
request destroy {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
event failed {
|
||||||
|
msg: str,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue