Transform Widget (#348)

This commit is contained in:
Pedro Burgos 2022-04-19 20:15:25 +02:00 committed by GitHub
parent d1bbe32625
commit 4ffb7d4841
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 218 additions and 7 deletions

View file

@ -29,6 +29,7 @@ All notable changes to eww will be listed here, starting at changes since versio
- Add drag and drop functionality to eventbox - Add drag and drop functionality to eventbox
- Add `search`, `captures`, `stringlength`, `arraylength` and `objectlength` functions for expressions (By: MartinJM, ElKowar) - Add `search`, `captures`, `stringlength`, `arraylength` and `objectlength` functions for expressions (By: MartinJM, ElKowar)
- Add `matches` function - Add `matches` function
- Add transform widget (By: druskus20)
### Notable Internal changes ### Notable Internal changes
- Rework state management completely, now making local state and dynamic widget hierarchy changes possible. - Rework state management completely, now making local state and dynamic widget hierarchy changes possible.

View file

@ -69,8 +69,8 @@ mod platform {
gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top); gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top);
gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom); gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom);
let xoffset = geometry.offset.x.relative_to(monitor.width); let xoffset = geometry.offset.x.pixels_relative_to(monitor.width);
let yoffset = geometry.offset.y.relative_to(monitor.height); let yoffset = geometry.offset.y.pixels_relative_to(monitor.height);
if left { if left {
gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset); gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset);
@ -161,8 +161,8 @@ mod platform {
let mon_end_y = (monitor_rect.y + monitor_rect.height) as u32 - 1u32; let mon_end_y = (monitor_rect.y + monitor_rect.height) as u32 - 1u32;
let dist = match strut_def.side { let dist = match strut_def.side {
Side::Left | Side::Right => strut_def.dist.relative_to(monitor_rect.width) as u32, Side::Left | Side::Right => strut_def.dist.pixels_relative_to(monitor_rect.width) as u32,
Side::Top | Side::Bottom => strut_def.dist.relative_to(monitor_rect.height) as u32, Side::Top | Side::Bottom => strut_def.dist.pixels_relative_to(monitor_rect.height) as u32,
}; };
// don't question it,..... // don't question it,.....

View file

@ -4,6 +4,7 @@ pub mod build_widget;
pub mod circular_progressbar; pub mod circular_progressbar;
pub mod def_widget_macro; pub mod def_widget_macro;
pub mod graph; pub mod graph;
pub mod transform;
pub mod widget_definitions; pub mod widget_definitions;
/// Run a command that was provided as an attribute. /// Run a command that was provided as an attribute.

View file

@ -0,0 +1,181 @@
use anyhow::{anyhow, Result};
use glib::{object_subclass, wrapper};
use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, str::FromStr};
use yuck::value::NumWithUnit;
use crate::error_handling_ctx;
wrapper! {
pub struct Transform(ObjectSubclass<TransformPriv>)
@extends gtk::Bin, gtk::Container, gtk::Widget;
}
pub struct TransformPriv {
rotate: RefCell<f64>,
translate_x: RefCell<Option<String>>,
translate_y: RefCell<Option<String>>,
scale_x: RefCell<Option<String>>,
scale_y: RefCell<Option<String>>,
content: RefCell<Option<gtk::Widget>>,
}
// This should match the default values from the ParamSpecs
impl Default for TransformPriv {
fn default() -> Self {
TransformPriv {
rotate: RefCell::new(0.0),
translate_x: RefCell::new(None),
translate_y: RefCell::new(None),
scale_x: RefCell::new(None),
scale_y: RefCell::new(None),
content: RefCell::new(None),
}
}
}
impl ObjectImpl for TransformPriv {
fn properties() -> &'static [glib::ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpec::new_double(
"rotate",
"Rotate",
"The Rotation",
f64::MIN,
f64::MAX,
0f64,
glib::ParamFlags::READWRITE,
),
glib::ParamSpec::new_string("translate-x", "Translate x", "The X Translation", None, glib::ParamFlags::READWRITE),
glib::ParamSpec::new_string("translate-y", "Translate y", "The Y Translation", None, glib::ParamFlags::READWRITE),
glib::ParamSpec::new_string("scale-x", "Scale x", "The amount to scale in x", None, glib::ParamFlags::READWRITE),
glib::ParamSpec::new_string("scale-y", "Scale y", "The amount to scale in y", None, glib::ParamFlags::READWRITE),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, obj: &Self::Type, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"rotate" => {
self.rotate.replace(value.get().unwrap());
obj.queue_draw(); // Queue a draw call with the updated value
}
"translate-x" => {
self.translate_x.replace(value.get().unwrap());
obj.queue_draw(); // Queue a draw call with the updated value
}
"translate-y" => {
self.translate_y.replace(value.get().unwrap());
obj.queue_draw(); // Queue a draw call with the updated value
}
"scale-x" => {
self.scale_x.replace(value.get().unwrap());
obj.queue_draw(); // Queue a draw call with the updated value
}
"scale-y" => {
self.scale_y.replace(value.get().unwrap());
obj.queue_draw(); // Queue a draw call with the updated value
}
x => panic!("Tried to set inexistant property of Transform: {}", x,),
}
}
fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"rotate" => self.rotate.borrow().to_value(),
"translate_x" => self.translate_x.borrow().to_value(),
"translate_y" => self.translate_y.borrow().to_value(),
"scale_x" => self.scale_x.borrow().to_value(),
"scale_y" => self.scale_y.borrow().to_value(),
x => panic!("Tried to access inexistant property of Transform: {}", x,),
}
}
}
#[object_subclass]
impl ObjectSubclass for TransformPriv {
type ParentType = gtk::Bin;
type Type = Transform;
const NAME: &'static str = "Transform";
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("transform");
}
}
impl Transform {
pub fn new() -> Self {
glib::Object::new::<Self>(&[]).expect("Failed to create Transform Widget")
}
}
impl ContainerImpl for TransformPriv {
fn add(&self, container: &Self::Type, widget: &gtk::Widget) {
if let Some(content) = &*self.content.borrow() {
// TODO: Handle this error when populating children widgets instead
error_handling_ctx::print_error(anyhow!("Error, trying to add multiple children to a circular-progress widget"));
self.parent_remove(container, content);
}
self.parent_add(container, widget);
self.content.replace(Some(widget.clone()));
}
}
impl BinImpl for TransformPriv {}
impl WidgetImpl for TransformPriv {
fn draw(&self, widget: &Self::Type, cr: &cairo::Context) -> Inhibit {
let res: Result<()> = try {
let rotate = *self.rotate.borrow();
let total_width = widget.allocated_width() as f64;
let total_height = widget.allocated_height() as f64;
cr.save()?;
let translate_x = match &*self.translate_x.borrow() {
Some(tx) => NumWithUnit::from_str(&tx)?.pixels_relative_to(total_width as i32) as f64,
None => 0.0,
};
let translate_y = match &*self.translate_y.borrow() {
Some(ty) => NumWithUnit::from_str(&ty)?.pixels_relative_to(total_height as i32) as f64,
None => 0.0,
};
let scale_x = match &*self.scale_x.borrow() {
Some(sx) => NumWithUnit::from_str(&sx)?.perc_relative_to(total_width as i32) as f64 / 100.0,
None => 1.0,
};
let scale_y = match &*self.scale_y.borrow() {
Some(sy) => NumWithUnit::from_str(&sy)?.perc_relative_to(total_height as i32) as f64 / 100.0,
None => 1.0,
};
cr.scale(scale_x, scale_y);
cr.rotate(perc_to_rad(rotate));
cr.translate(translate_x, translate_y);
// Children widget
if let Some(child) = &*self.content.borrow() {
widget.propagate_draw(child, &cr);
}
cr.restore()?;
};
if let Err(error) = res {
error_handling_ctx::print_error(error)
};
gtk::Inhibit(false)
}
}
fn perc_to_rad(n: f64) -> f64 {
(n / 100f64) * 2f64 * std::f64::consts::PI
}

View file

@ -1,5 +1,5 @@
#![allow(clippy::option_map_unit_fn)] #![allow(clippy::option_map_unit_fn)]
use super::{build_widget::BuilderArgs, circular_progressbar::*, run_command}; use super::{build_widget::BuilderArgs, circular_progressbar::*, transform::*, run_command};
use crate::{ use crate::{
def_widget, enum_parse, def_widget, enum_parse,
error::DiagError, error::DiagError,
@ -60,6 +60,7 @@ pub(super) fn widget_use_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<gtk::W
"eventbox" => build_gtk_event_box(bargs)?.upcast(), "eventbox" => build_gtk_event_box(bargs)?.upcast(),
"circular-progress" => build_circular_progress_bar(bargs)?.upcast(), "circular-progress" => build_circular_progress_bar(bargs)?.upcast(),
"graph" => build_graph(bargs)?.upcast(), "graph" => build_graph(bargs)?.upcast(),
"transform" => build_transform(bargs)?.upcast(),
"scale" => build_gtk_scale(bargs)?.upcast(), "scale" => build_gtk_scale(bargs)?.upcast(),
"progress" => build_gtk_progress(bargs)?.upcast(), "progress" => build_gtk_progress(bargs)?.upcast(),
"image" => build_gtk_image(bargs)?.upcast(), "image" => build_gtk_image(bargs)?.upcast(),
@ -779,6 +780,26 @@ fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result<gtk::Calendar> {
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget transform
/// @desc A widget that applies transformations to its content. They are applied in the following
/// order: rotate->translate->scale)
fn build_transform(bargs: &mut BuilderArgs) -> Result<Transform> {
let w = Transform::new();
def_widget!(bargs, _g, w, {
// @prop rotation - the percentage to rotate
prop(rotate: as_f64) { w.set_property("rotate", rotate)?; },
// @prop translate-x - the amount to translate in the x direction (px or %)
prop(translate_x: as_string) { w.set_property("translate-x", translate_x)?; },
// @prop translate-y - the amount to translate in the y direction (px or %)
prop(translate_y: as_string) { w.set_property("translate-y", translate_y)?; },
// @prop scale_x - the amount to scale in the x direction (px or %)
prop(scale_x: as_string) { w.set_property("scale-x", scale_x)?; },
// @prop scale_y - the amount to scale in the y direction (px or %)
prop(scale_y: as_string) { w.set_property("scale-y", scale_y)?; },
});
Ok(w)
}
/// @widget circular-progress /// @widget circular-progress
/// @desc A widget that displays a circular progress bar /// @desc A widget that displays a circular progress bar
fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result<CircProg> { fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result<CircProg> {

View file

@ -26,12 +26,19 @@ pub enum NumWithUnit {
} }
impl NumWithUnit { impl NumWithUnit {
pub fn relative_to(&self, max: i32) -> i32 { pub fn pixels_relative_to(&self, max: i32) -> i32 {
match *self { match *self {
NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32, NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32,
NumWithUnit::Pixels(n) => n, NumWithUnit::Pixels(n) => n,
} }
} }
pub fn perc_relative_to(&self, max: i32) -> i32 {
match *self {
NumWithUnit::Percent(n) => n,
NumWithUnit::Pixels(n) => ((n as f64 / max as f64) * 100.0) as i32,
}
}
} }
impl FromStr for NumWithUnit { impl FromStr for NumWithUnit {
@ -86,7 +93,7 @@ impl Coords {
/// resolve the possibly relative coordinates relative to a given containers size /// resolve the possibly relative coordinates relative to a given containers size
pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) { pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) {
(self.x.relative_to(width), self.y.relative_to(height)) (self.x.pixels_relative_to(width), self.y.pixels_relative_to(height))
} }
} }

BIN
out.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB