Transform Widget (#348)
This commit is contained in:
parent
d1bbe32625
commit
4ffb7d4841
7 changed files with 218 additions and 7 deletions
|
@ -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 `search`, `captures`, `stringlength`, `arraylength` and `objectlength` functions for expressions (By: MartinJM, ElKowar)
|
||||
- Add `matches` function
|
||||
- Add transform widget (By: druskus20)
|
||||
|
||||
### Notable Internal changes
|
||||
- Rework state management completely, now making local state and dynamic widget hierarchy changes possible.
|
||||
|
|
|
@ -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::Bottom, bottom);
|
||||
|
||||
let xoffset = geometry.offset.x.relative_to(monitor.width);
|
||||
let yoffset = geometry.offset.y.relative_to(monitor.height);
|
||||
let xoffset = geometry.offset.x.pixels_relative_to(monitor.width);
|
||||
let yoffset = geometry.offset.y.pixels_relative_to(monitor.height);
|
||||
|
||||
if left {
|
||||
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 dist = match strut_def.side {
|
||||
Side::Left | Side::Right => strut_def.dist.relative_to(monitor_rect.width) as u32,
|
||||
Side::Top | Side::Bottom => strut_def.dist.relative_to(monitor_rect.height) as u32,
|
||||
Side::Left | Side::Right => strut_def.dist.pixels_relative_to(monitor_rect.width) as u32,
|
||||
Side::Top | Side::Bottom => strut_def.dist.pixels_relative_to(monitor_rect.height) as u32,
|
||||
};
|
||||
|
||||
// don't question it,.....
|
||||
|
|
|
@ -4,6 +4,7 @@ pub mod build_widget;
|
|||
pub mod circular_progressbar;
|
||||
pub mod def_widget_macro;
|
||||
pub mod graph;
|
||||
pub mod transform;
|
||||
pub mod widget_definitions;
|
||||
|
||||
/// Run a command that was provided as an attribute.
|
||||
|
|
181
crates/eww/src/widgets/transform.rs
Normal file
181
crates/eww/src/widgets/transform.rs
Normal 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: >k::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
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#![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::{
|
||||
def_widget, enum_parse,
|
||||
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(),
|
||||
"circular-progress" => build_circular_progress_bar(bargs)?.upcast(),
|
||||
"graph" => build_graph(bargs)?.upcast(),
|
||||
"transform" => build_transform(bargs)?.upcast(),
|
||||
"scale" => build_gtk_scale(bargs)?.upcast(),
|
||||
"progress" => build_gtk_progress(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)
|
||||
}
|
||||
|
||||
/// @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
|
||||
/// @desc A widget that displays a circular progress bar
|
||||
fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result<CircProg> {
|
||||
|
|
|
@ -26,12 +26,19 @@ pub enum NumWithUnit {
|
|||
}
|
||||
|
||||
impl NumWithUnit {
|
||||
pub fn relative_to(&self, max: i32) -> i32 {
|
||||
pub fn pixels_relative_to(&self, max: i32) -> i32 {
|
||||
match *self {
|
||||
NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32,
|
||||
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 {
|
||||
|
@ -86,7 +93,7 @@ impl Coords {
|
|||
|
||||
/// resolve the possibly relative coordinates relative to a given containers size
|
||||
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
BIN
out.gif
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
Loading…
Add table
Reference in a new issue