Implement relative window size and position. closes #30
This commit is contained in:
parent
0207ccaf3d
commit
423c79d263
8 changed files with 132 additions and 51 deletions
|
@ -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"
|
||||
|
|
48
src/app.rs
48
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<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: >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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")]
|
||||
|
|
27
src/util.rs
27
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<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
81
src/value/coords.rs
Normal 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()?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue