Implement relative window size and position. closes #30

This commit is contained in:
elkowar 2020-10-20 18:44:50 +02:00
parent 0207ccaf3d
commit 423c79d263
8 changed files with 132 additions and 51 deletions

View file

@ -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"

View file

@ -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<util::Coords>,
size: Option<util::Coords>,
) -> Result<()> {
fn open_window(&mut self, window_name: &config::WindowName, pos: Option<Coords>, size: Option<Coords>) -> 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: &gtk::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,
},
}
}

View file

@ -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<i32>,
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::<WindowStacking>())
.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,
})
}
}

View file

@ -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<util::Coords>,
pos: Option<Coords>,
#[structopt(short, long, help = "The size of the window to open")]
size: Option<util::Coords>,
size: Option<Coords>,
},
#[structopt(name = "close", help = "close the window with the given name")]

View file

@ -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<std::time::Duration> {
}
}
#[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<Self> {
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.

81
src/value/coords.rs Normal file
View file

@ -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<Self, Self::Err> {
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::<i32>()?;
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<Self, Self::Err> {
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<Coords> {
Ok(Coords {
x: x.parse()?,
y: y.parse()?,
})
}
}

View file

@ -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

View file

@ -227,7 +227,7 @@ fn build_gtk_box(bargs: &mut BuilderArgs) -> Result<gtk::Box> {
// @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)
}