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"
|
derive_more = "0.99"
|
||||||
maplit = "1"
|
maplit = "1"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
#ipc-channel="0.14.1"
|
|
||||||
serde = {version = "1.0", features = ["derive"]}
|
serde = {version = "1.0", features = ["derive"]}
|
||||||
extend = "0.3.0"
|
extend = "0.3.0"
|
||||||
grass = "0.10"
|
grass = "0.10"
|
||||||
|
|
|
||||||
48
src/app.rs
48
src/app.rs
|
|
@ -4,8 +4,7 @@ use crate::{
|
||||||
eww_state,
|
eww_state,
|
||||||
script_var_handler::*,
|
script_var_handler::*,
|
||||||
util,
|
util,
|
||||||
util::Coords,
|
value::{Coords, NumWithUnit, PrimitiveValue, VarName},
|
||||||
value::{PrimitiveValue, VarName},
|
|
||||||
widgets,
|
widgets,
|
||||||
};
|
};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
|
@ -14,6 +13,7 @@ use debug_stub_derive::*;
|
||||||
use gdk::WindowExt;
|
use gdk::WindowExt;
|
||||||
use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt};
|
use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -98,12 +98,7 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(&mut self, window_name: &config::WindowName, pos: Option<Coords>, size: Option<Coords>) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
window_name: &config::WindowName,
|
|
||||||
pos: Option<util::Coords>,
|
|
||||||
size: Option<util::Coords>,
|
|
||||||
) -> Result<()> {
|
|
||||||
// remove and close existing window with the same name
|
// remove and close existing window with the same name
|
||||||
let _ = self.close_window(window_name);
|
let _ = self.close_window(window_name);
|
||||||
|
|
||||||
|
|
@ -114,17 +109,26 @@ impl App {
|
||||||
.context(format!("No window named '{}' defined", window_name))?
|
.context(format!("No window named '{}' defined", window_name))?
|
||||||
.clone();
|
.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.position = pos.unwrap_or_else(|| window_def.position);
|
||||||
window_def.size = size.unwrap_or_else(|| window_def.size);
|
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);
|
let window = gtk::Window::new(gtk::WindowType::Popup);
|
||||||
window.set_title(&format!("Eww - {}", window_name));
|
window.set_title(&format!("Eww - {}", window_name));
|
||||||
let wm_class_name = format!("eww-{}", window_name);
|
let wm_class_name = format!("eww-{}", window_name);
|
||||||
window.set_wmclass(&wm_class_name, &wm_class_name);
|
window.set_wmclass(&wm_class_name, &wm_class_name);
|
||||||
window.set_type_hint(gdk::WindowTypeHint::Dock);
|
window.set_type_hint(gdk::WindowTypeHint::Dock);
|
||||||
window.set_position(gtk::WindowPosition::Center);
|
window.set_position(gtk::WindowPosition::Center);
|
||||||
window.set_default_size(window_def.size.0, window_def.size.1);
|
window.set_default_size(actual_window_rect.width, actual_window_rect.height);
|
||||||
window.set_size_request(window_def.size.0, window_def.size.1);
|
window.set_size_request(actual_window_rect.width, actual_window_rect.height);
|
||||||
window.set_decorated(false);
|
window.set_decorated(false);
|
||||||
window.set_resizable(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")?;
|
let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?;
|
||||||
gdk_window.set_override_redirect(true);
|
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();
|
gdk_window.show();
|
||||||
|
|
||||||
if window_def.stacking == WindowStacking::Foreground {
|
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());
|
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::{
|
use crate::{
|
||||||
util,
|
util,
|
||||||
value::{PrimitiveValue, VarName},
|
value::{Coords, PrimitiveValue, VarName},
|
||||||
};
|
};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use derive_more;
|
use derive_more;
|
||||||
use element::*;
|
use element::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
use util::Coords;
|
|
||||||
use xml_ext::*;
|
use xml_ext::*;
|
||||||
|
|
||||||
pub mod element;
|
pub mod element;
|
||||||
|
|
@ -204,6 +203,7 @@ pub struct EwwWindowDefinition {
|
||||||
pub position: Coords,
|
pub position: Coords,
|
||||||
pub size: Coords,
|
pub size: Coords,
|
||||||
pub stacking: WindowStacking,
|
pub stacking: WindowStacking,
|
||||||
|
pub screen_number: Option<i32>,
|
||||||
pub widget: WidgetUse,
|
pub widget: WidgetUse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,16 +212,12 @@ impl EwwWindowDefinition {
|
||||||
ensure_xml_tag_is!(xml, "window");
|
ensure_xml_tag_is!(xml, "window");
|
||||||
|
|
||||||
let size_node = xml.child("size")?;
|
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 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
|
let stacking = xml.attr("stacking").ok().map(|x| x.parse()).transpose()?.unwrap_or_default();
|
||||||
.attr("stacking")
|
let screen_number = xml.attr("screen").ok().map(|x| x.parse()).transpose()?;
|
||||||
.ok()
|
|
||||||
.map(|stacking| stacking.parse::<WindowStacking>())
|
|
||||||
.transpose()?
|
|
||||||
.unwrap_or_else(WindowStacking::default);
|
|
||||||
|
|
||||||
let widget = WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?;
|
let widget = WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?;
|
||||||
Ok(EwwWindowDefinition {
|
Ok(EwwWindowDefinition {
|
||||||
|
|
@ -229,6 +225,7 @@ impl EwwWindowDefinition {
|
||||||
size,
|
size,
|
||||||
widget,
|
widget,
|
||||||
stacking,
|
stacking,
|
||||||
|
screen_number,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
use value::Coords;
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
@ -90,10 +91,10 @@ pub enum OptAction {
|
||||||
window_name: config::WindowName,
|
window_name: config::WindowName,
|
||||||
|
|
||||||
#[structopt(short, long, help = "The position of the window, where it should open.")]
|
#[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")]
|
#[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")]
|
#[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 extend::ext;
|
||||||
use grass;
|
use grass;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use std::path::Path;
|
||||||
use std::{fmt, path::Path};
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_try_from {
|
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
|
/// 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
|
/// by the actual env-variables. If the env-var isn't found, will replace the
|
||||||
/// reference with an empty string.
|
/// 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;
|
use std::fmt;
|
||||||
|
|
||||||
pub mod attr_value;
|
pub mod attr_value;
|
||||||
|
pub mod coords;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub use attr_value::*;
|
pub use attr_value::*;
|
||||||
|
pub use coords::*;
|
||||||
pub use primitive::*;
|
pub use primitive::*;
|
||||||
|
|
||||||
/// The name of a variable
|
/// 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 - orientation of the box. possible values: $orientation
|
||||||
prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) },
|
prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) },
|
||||||
// @prop space-evenly - space the widgets evenly.
|
// @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)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue