GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Implement window opacity per window layout mode
src/backend/window_runner.rs | 21 ++++++++++
src/color.rs | 91 ++++++++++++++++++++++++++++++++++++++++++
src/scale.rs | 33 +++++++++++++++
src/widget/container/window.rs | 6 ++-
4 files changed, 150 insertions(+), 1 deletion(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 25f95cf..f957310 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -938,6 +938,9 @@ pub trait Application: Sized + 'static {
fn overlay_quads(&mut self, _quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, _size: LogicalSize, _scale: f64) {}
fn text_items(&self) -> &[TextItem];
fn render_popovers(&self, _pc: &mut dyn crate::layout::RenderTarget) {}
+ fn input_regions(&self) -> Option<Vec<(i32, i32, i32, i32)>> {
+ None
+ }
fn ui_context(&self) -> Option<&crate::context::UiContext> {
None
@@ -1184,6 +1187,18 @@ impl<A: Application> EngineState<A> {
let mut quads = Vec::new();
self.inner.view(&mut quads, LogicalSize::new(logical_w, logical_h), scale_factor);
+
+ if let Some(ref surface) = self.surface {
+ if let Some(regions) = self.inner.input_regions() {
+ let compositor = self.compositor_state.wl_compositor();
+ let wl_region = compositor.create_region(&self.qh, ());
+ for &(rx, ry, rw, rh) in ®ions {
+ wl_region.add(rx, ry, rw, rh);
+ }
+ surface.set_input_region(Some(&wl_region));
+ wl_region.destroy();
+ }
+ }
let mut rounded_quads = Vec::new();
self.inner.view_rounded_quads(&mut rounded_quads, LogicalSize::new(logical_w, logical_h), scale_factor);
@@ -1571,6 +1586,11 @@ impl<A: Application> WindowHandler for EngineState<A> {
configure: WindowConfigure,
_serial: u32,
) {
+ let is_fs = configure.is_fullscreen();
+ let is_max = configure.is_maximized();
+ crate::scale::set_fullscreen(is_fs);
+ crate::scale::set_maximized(is_max);
+
let (w, h) = configure.new_size;
if let (Some(w), Some(h)) = (w, h) {
let width = w.get();
@@ -2068,6 +2088,7 @@ pub fn run<A: Application>() {
let inner = A::new(&qh, sender.clone());
let settings = inner.settings();
+ crate::scale::set_app_id(settings.app_id.clone());
let mut engine_state = EngineState {
registry_state: RegistryState::new(&globals),
diff --git a/src/color.rs b/src/color.rs
index 11f342c..ce43de7 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -508,3 +508,94 @@ pub fn active_theme() -> Theme {
hover_overlay: [1.0, 1.0, 1.0, 0.08],
}
}
+
+pub fn active_window_mode() -> String {
+ let app_id = crate::scale::app_id();
+ if app_id.is_empty() {
+ return "floating".to_string();
+ }
+
+ let config_path = "/home/lsgalante/.config/cce/config.json";
+ let content = match std::fs::read_to_string(config_path) {
+ Ok(c) => c,
+ Err(_) => return "floating".to_string(),
+ };
+ let val: serde_json::Value = match serde_json::from_str(&content) {
+ Ok(v) => v,
+ Err(_) => return "floating".to_string(),
+ };
+
+ if let Some(mode_rules) = val.get("mode_rule").and_then(|r| r.as_array()) {
+ for rule in mode_rules {
+ if rule.get("app_id").and_then(|id| id.as_str()) == Some(&app_id) {
+ if let Some(mode) = rule.get("mode").and_then(|m| m.as_str()) {
+ return mode.to_string();
+ }
+ }
+ }
+ }
+
+ let mut default_tile_mode = "cascade".to_string();
+ if let Some(tag_layouts) = val.get("tag_layout").and_then(|l| l.as_array()) {
+ for tl in tag_layouts {
+ if tl.get("tag").and_then(|t| t.as_u64()) == Some(1) {
+ if let Some(m) = tl.get("mode").and_then(|m| m.as_str()) {
+ default_tile_mode = m.to_string();
+ }
+ }
+ }
+ }
+
+ if crate::scale::is_fullscreen() {
+ return "fullscreen".to_string();
+ }
+ if crate::scale::is_maximized() {
+ return default_tile_mode;
+ }
+
+ "floating".to_string()
+}
+
+pub fn active_window_opacity() -> f32 {
+ let mode = active_window_mode();
+ let config_path = "/home/lsgalante/.config/cce/config.json";
+ let content = match std::fs::read_to_string(config_path) {
+ Ok(c) => c,
+ Err(_) => return 0.9,
+ };
+ let val: serde_json::Value = match serde_json::from_str(&content) {
+ Ok(v) => v,
+ Err(_) => return 0.9,
+ };
+
+ let key = match mode.as_str() {
+ "fullscreen" => "fullscreen_opacity",
+ "cascade" => "cascade_opacity",
+ "grid" => "grid_opacity",
+ "floating" => "floating_opacity",
+ "side-panel" | "pinned" => "pinned_opacity",
+ "popup" => "popup_opacity",
+ _ => "window_opacity",
+ };
+
+ if let Some(opacity) = val.pointer(&format!("/layout/{}", key)).and_then(|v| v.as_f64()) {
+ return opacity as f32;
+ }
+
+ // Fallbacks if not present:
+ match key {
+ "fullscreen_opacity" => 0.95,
+ "cascade_opacity" => 0.05,
+ "grid_opacity" => 0.05,
+ "floating_opacity" => 0.9,
+ "pinned_opacity" => 0.05,
+ "popup_opacity" => 0.20,
+ _ => {
+ val.pointer("/surfaces/window_opacity")
+ .and_then(|v| v.as_f64())
+ .map(|v| v as f32)
+ .unwrap_or(0.9)
+ }
+ }
+}
+
diff --git a/src/scale.rs b/src/scale.rs
index 5c4b475..f0b8eda 100644
--- a/src/scale.rs
+++ b/src/scale.rs
@@ -1,6 +1,9 @@
use std::sync::RwLock;
static SCALE_FACTOR: RwLock<f32> = RwLock::new(1.0);
+static APP_ID: RwLock<String> = RwLock::new(String::new());
+static FULLSCREEN: RwLock<bool> = RwLock::new(false);
+static MAXIMIZED: RwLock<bool> = RwLock::new(false);
pub fn scale_factor() -> f32 {
*SCALE_FACTOR.read().unwrap()
@@ -11,3 +14,33 @@ pub fn set_scale_factor(scale: f32) {
*lock = scale;
}
}
+
+pub fn app_id() -> String {
+ APP_ID.read().unwrap().clone()
+}
+
+pub fn set_app_id(id: String) {
+ if let Ok(mut lock) = APP_ID.write() {
+ *lock = id;
+ }
+}
+
+pub fn is_fullscreen() -> bool {
+ *FULLSCREEN.read().unwrap()
+}
+
+pub fn set_fullscreen(fs: bool) {
+ if let Ok(mut lock) = FULLSCREEN.write() {
+ *lock = fs;
+ }
+}
+
+pub fn is_maximized() -> bool {
+ *MAXIMIZED.read().unwrap()
+}
+
+pub fn set_maximized(m: bool) {
+ if let Ok(mut lock) = MAXIMIZED.write() {
+ *lock = m;
+ }
+}
diff --git a/src/widget/container/window.rs b/src/widget/container/window.rs
index 4ee5bfe..2693882 100644
--- a/src/widget/container/window.rs
+++ b/src/widget/container/window.rs
@@ -89,7 +89,11 @@ impl Element for Window {
}
fn color(&self) -> [f32; 4] {
- self.background_color.unwrap_or([0.0, 0.0, 0.0, 0.0])
+ let mut base_color = self.background_color.unwrap_or([0.0, 0.0, 0.0, 0.0]);
+ if base_color[3] > 0.001 {
+ base_color[3] = crate::color::active_window_opacity();
+ }
+ base_color
}
fn visible(&self) -> bool {