From 423c79d26398ae7d9b9403affbf1a37cb2a8edb2 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Oct 2020 18:44:50 +0200 Subject: [PATCH] Implement relative window size and position. closes #30 --- Cargo.toml | 1 - src/app.rs | 48 +++++++++++++----- src/config/mod.rs | 17 +++---- src/main.rs | 5 +- src/util.rs | 27 +---------- src/value/coords.rs | 81 +++++++++++++++++++++++++++++++ src/value/mod.rs | 2 + src/widgets/widget_definitions.rs | 2 +- 8 files changed, 132 insertions(+), 51 deletions(-) create mode 100644 src/value/coords.rs diff --git a/Cargo.toml b/Cargo.toml index 0bb7104..d78b40d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ anyhow = "1.0" derive_more = "0.99" maplit = "1" structopt = "0.3" -#ipc-channel="0.14.1" serde = {version = "1.0", features = ["derive"]} extend = "0.3.0" grass = "0.10" diff --git a/src/app.rs b/src/app.rs index bcf6859..5fadbd3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,8 +4,7 @@ use crate::{ eww_state, script_var_handler::*, util, - util::Coords, - value::{PrimitiveValue, VarName}, + value::{Coords, NumWithUnit, PrimitiveValue, VarName}, widgets, }; use anyhow::*; @@ -14,6 +13,7 @@ use debug_stub_derive::*; use gdk::WindowExt; use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use itertools::Itertools; + use std::collections::HashMap; #[derive(Debug)] @@ -98,12 +98,7 @@ impl App { Ok(()) } - fn open_window( - &mut self, - window_name: &config::WindowName, - pos: Option, - size: Option, - ) -> Result<()> { + fn open_window(&mut self, window_name: &config::WindowName, pos: Option, size: Option) -> Result<()> { // remove and close existing window with the same name let _ = self.close_window(window_name); @@ -114,17 +109,26 @@ impl App { .context(format!("No window named '{}' defined", window_name))? .clone(); + let display = gdk::Display::get_default().expect("could not get default display"); + let screen_number = &window_def + .screen_number + .unwrap_or(display.get_default_screen().get_primary_monitor()); + + let monitor_geometry = display.get_default_screen().get_monitor_geometry(*screen_number); + window_def.position = pos.unwrap_or_else(|| window_def.position); window_def.size = size.unwrap_or_else(|| window_def.size); + let actual_window_rect = get_window_rectangle_in_screen(monitor_geometry, window_def.position, window_def.size); + let window = gtk::Window::new(gtk::WindowType::Popup); window.set_title(&format!("Eww - {}", window_name)); let wm_class_name = format!("eww-{}", window_name); window.set_wmclass(&wm_class_name, &wm_class_name); window.set_type_hint(gdk::WindowTypeHint::Dock); window.set_position(gtk::WindowPosition::Center); - window.set_default_size(window_def.size.0, window_def.size.1); - window.set_size_request(window_def.size.0, window_def.size.1); + window.set_default_size(actual_window_rect.width, actual_window_rect.height); + window.set_size_request(actual_window_rect.width, actual_window_rect.height); window.set_decorated(false); window.set_resizable(false); @@ -151,7 +155,7 @@ impl App { let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?; gdk_window.set_override_redirect(true); - gdk_window.move_(window_def.position.0, window_def.position.1); + gdk_window.move_(actual_window_rect.x, actual_window_rect.y); gdk_window.show(); if window_def.stacking == WindowStacking::Foreground { @@ -208,3 +212,25 @@ fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { }); window.set_visual(visual.as_ref()); } + +/// Calculate the window rectangle given the configured window [`pos`] and [`size`], which might be relative to the screen size. +fn get_window_rectangle_in_screen(screen_rect: gdk::Rectangle, pos: Coords, size: Coords) -> gdk::Rectangle { + gdk::Rectangle { + x: match pos.x { + NumWithUnit::Percent(n) => (screen_rect.width as f64 / 100.0).floor() as i32 * n, + NumWithUnit::Pixels(n) => screen_rect.x + n, + }, + y: match pos.y { + NumWithUnit::Percent(n) => (screen_rect.height as f64 / 100.0).floor() as i32 * n, + NumWithUnit::Pixels(n) => screen_rect.y + n, + }, + width: match size.x { + NumWithUnit::Percent(n) => (screen_rect.width as f64 / 100.0).floor() as i32 * n, + NumWithUnit::Pixels(n) => n, + }, + height: match size.y { + NumWithUnit::Percent(n) => (screen_rect.height as f64 / 100.0).floor() as i32 * n, + NumWithUnit::Pixels(n) => n, + }, + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index eb58235..3aafcae 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,13 +1,12 @@ use crate::{ util, - value::{PrimitiveValue, VarName}, + value::{Coords, PrimitiveValue, VarName}, }; use anyhow::*; use derive_more; use element::*; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; -use util::Coords; use xml_ext::*; pub mod element; @@ -204,6 +203,7 @@ pub struct EwwWindowDefinition { pub position: Coords, pub size: Coords, pub stacking: WindowStacking, + pub screen_number: Option, pub widget: WidgetUse, } @@ -212,16 +212,12 @@ impl EwwWindowDefinition { ensure_xml_tag_is!(xml, "window"); let size_node = xml.child("size")?; - let size = Coords(size_node.attr("x")?.parse()?, size_node.attr("y")?.parse()?); + let size = Coords::from_strs(size_node.attr("x")?, size_node.attr("y")?)?; let pos_node = xml.child("pos")?; - let position = Coords(pos_node.attr("x")?.parse()?, pos_node.attr("y")?.parse()?); + let position = Coords::from_strs(pos_node.attr("x")?, pos_node.attr("y")?)?; - let stacking = xml - .attr("stacking") - .ok() - .map(|stacking| stacking.parse::()) - .transpose()? - .unwrap_or_else(WindowStacking::default); + let stacking = xml.attr("stacking").ok().map(|x| x.parse()).transpose()?.unwrap_or_default(); + let screen_number = xml.attr("screen").ok().map(|x| x.parse()).transpose()?; let widget = WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?; Ok(EwwWindowDefinition { @@ -229,6 +225,7 @@ impl EwwWindowDefinition { size, widget, stacking, + screen_number, }) } } diff --git a/src/main.rs b/src/main.rs index e119169..758c7f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ use std::{ path::{Path, PathBuf}, }; use structopt::StructOpt; +use value::Coords; pub mod app; pub mod config; @@ -90,10 +91,10 @@ pub enum OptAction { window_name: config::WindowName, #[structopt(short, long, help = "The position of the window, where it should open.")] - pos: Option, + pos: Option, #[structopt(short, long, help = "The size of the window to open")] - size: Option, + size: Option, }, #[structopt(name = "close", help = "close the window with the given name")] diff --git a/src/util.rs b/src/util.rs index abd8d7f..3217f68 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,8 +2,7 @@ use anyhow::*; use extend::ext; use grass; use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use std::{fmt, path::Path}; +use std::path::Path; #[macro_export] macro_rules! impl_try_from { @@ -59,30 +58,6 @@ pub fn parse_duration(s: &str) -> Result { } } -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] -pub struct Coords(pub i32, pub i32); - -impl fmt::Display for Coords { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}x{}", self.0, self.1) - } -} - -impl std::str::FromStr for Coords { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let (x, y) = s.split_once('x').ok_or_else(|| anyhow!("must be formatted like 200x500"))?; - Ok(Coords(x.parse()?, y.parse()?)) - } -} - -impl From<(i32, i32)> for Coords { - fn from((x, y): (i32, i32)) -> Self { - Coords(x, y) - } -} - /// Replace all env-var references of the format `"something $foo"` in a string /// by the actual env-variables. If the env-var isn't found, will replace the /// reference with an empty string. diff --git a/src/value/coords.rs b/src/value/coords.rs new file mode 100644 index 0000000..f973bf6 --- /dev/null +++ b/src/value/coords.rs @@ -0,0 +1,81 @@ +use anyhow::*; +use serde::{Deserialize, Serialize}; +use std::{fmt, str::FromStr}; + +#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] +pub enum NumWithUnit { + Percent(i32), + Pixels(i32), +} + +impl fmt::Debug for NumWithUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + +impl fmt::Display for NumWithUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NumWithUnit::Percent(x) => write!(f, "{}%", x), + NumWithUnit::Pixels(x) => write!(f, "{}px", x), + } + } +} + +impl FromStr for NumWithUnit { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + lazy_static::lazy_static! { + static ref PATTERN: regex::Regex = regex::Regex::new("^(\\d+)(.*)$").unwrap(); + }; + + let captures = PATTERN.captures(s).with_context(|| format!("could not parse '{}'", s))?; + let value = captures.get(1).unwrap().as_str().parse::()?; + let value = match captures.get(2).unwrap().as_str() { + "px" | "" => NumWithUnit::Pixels(value), + "%" => NumWithUnit::Percent(value), + _ => bail!("couldn't parse {}, unit must be either px or %", s), + }; + Ok(value) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] +pub struct Coords { + pub x: NumWithUnit, + pub y: NumWithUnit, +} + +impl FromStr for Coords { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let (x, y) = s + .split_once(|x: char| x.to_ascii_lowercase() == 'x') + .ok_or_else(|| anyhow!("must be formatted like 200x500"))?; + Coords::from_strs(x, y) + } +} + +impl fmt::Display for Coords { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}X{}", self.x, self.y) + } +} + +impl fmt::Debug for Coords { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CoordsWithUnits({}, {})", self.x, self.y) + } +} + +impl Coords { + pub fn from_strs(x: &str, y: &str) -> Result { + Ok(Coords { + x: x.parse()?, + y: y.parse()?, + }) + } +} diff --git a/src/value/mod.rs b/src/value/mod.rs index 035f0db..241f0c9 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -4,8 +4,10 @@ use serde::{Deserialize, Serialize}; use std::fmt; pub mod attr_value; +pub mod coords; pub mod primitive; pub use attr_value::*; +pub use coords::*; pub use primitive::*; /// The name of a variable diff --git a/src/widgets/widget_definitions.rs b/src/widgets/widget_definitions.rs index f7f3bb6..5b718a3 100644 --- a/src/widgets/widget_definitions.rs +++ b/src/widgets/widget_definitions.rs @@ -227,7 +227,7 @@ fn build_gtk_box(bargs: &mut BuilderArgs) -> Result { // @prop orientation - orientation of the box. possible values: $orientation prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) }, // @prop space-evenly - space the widgets evenly. - prop(space_evenly: as_bool = true) { gtk_widget.set_homogeneous(space_evenly) }, + prop(space_evenly: as_bool = false) { gtk_widget.set_homogeneous(space_evenly) }, }); Ok(gtk_widget) }