Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement explicitly setting a center of rotation #783

Merged
merged 3 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All notable changes to eww will be listed here, starting at changes since versio
- Add scss support for the `:style` widget property (By: ovalkonia)
- Add `min` and `max` function calls to simplexpr (By: ovalkonia)
- Add `flip-x`, `flip-y`, `vertical` options to the graph widget to determine its direction
- Add `transform-origin-x`/`transform-origin-y` properties to transform widget (By: mario-kr)

## [0.6.0] (21.04.2024)

Expand Down
30 changes: 28 additions & 2 deletions crates/eww/src/widgets/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ pub struct TransformPriv {
#[property(get, set, nick = "Rotate", blurb = "The Rotation", minimum = f64::MIN, maximum = f64::MAX, default = 0f64)]
rotate: RefCell<f64>,

#[property(get, set, nick = "Transform-Origin X", blurb = "X coordinate (%/px) for the Transform-Origin", default = None)]
transform_origin_x: RefCell<Option<String>>,

#[property(get, set, nick = "Transform-Origin Y", blurb = "Y coordinate (%/px) for the Transform-Origin", default = None)]
transform_origin_y: RefCell<Option<String>>,

#[property(get, set, nick = "Translate x", blurb = "The X Translation", default = None)]
translate_x: RefCell<Option<String>>,

Expand All @@ -37,6 +43,8 @@ impl Default for TransformPriv {
fn default() -> Self {
TransformPriv {
rotate: RefCell::new(0.0),
transform_origin_x: RefCell::new(None),
transform_origin_y: RefCell::new(None),
translate_x: RefCell::new(None),
translate_y: RefCell::new(None),
scale_x: RefCell::new(None),
Expand All @@ -57,6 +65,14 @@ impl ObjectImpl for TransformPriv {
self.rotate.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"transform-origin-x" => {
self.transform_origin_x.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"transform-origin-y" => {
self.transform_origin_y.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"translate-x" => {
self.translate_x.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
Expand Down Expand Up @@ -128,6 +144,15 @@ impl WidgetImpl for TransformPriv {

cr.save()?;

let transform_origin_x = match &*self.transform_origin_x.borrow() {
Some(rcx) => NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64,
None => 0.0,
};
let transform_origin_y = match &*self.transform_origin_y.borrow() {
Some(rcy) => NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64,
None => 0.0,
};

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,
Expand All @@ -148,9 +173,10 @@ impl WidgetImpl for TransformPriv {
None => 1.0,
};

cr.scale(scale_x, scale_y);
cr.translate(transform_origin_x, transform_origin_y);
cr.rotate(perc_to_rad(rotate));
cr.translate(translate_x, translate_y);
cr.translate(translate_x - transform_origin_x, translate_y - transform_origin_y);
Comment on lines +176 to +178
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't break the order defined here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, even before this patch, the methods called on cr are in the order:

  1. scale
  2. rotate
  3. translate

whereas in the comment you linked, it's claimed that the order is:

  1. rotate
  2. translate
  3. scale

Sooooo... I don't know? Was it ever in the right order? Or is it sorted by the cairo-Context?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't sort them, if i'm understanding the docs right.
i suggest sorting them in the order described in the documentation, though i don't think it makes much of a difference.

also, the only thing causing the merge conflict is the changelog entry, that should be trivial to resolve when rebasing onto master ^^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot from 2024-08-29 17-46-53

There is a difference I'm afraid.
On the left side, scale is called after rotate + translate. basically, it warps the contents into a parallelogram when rotated. (the scaling is applied in the original directions of the x and y axis)
On the right is the same eww.yuck, but scale is called before rotate+translate, or the whole, scaled widget is rotated.

Generally speaking, users can always force a specific order by nesting multiple transforms. I did this here, where i nested the rotate+translate transform inside the scale transform:
Screenshot from 2024-08-29 18-00-03
but it's not entirely the same; the bottom seems to be cut off (Because the size on the y-axis for this widget was limited by the scale to 60%)

soo... do we want the warping rotate as the default (scale after rotate, as documented), or do we want the non-warping rotate as the default (as previously implemented)?

Copy link
Contributor

@w-lfchen w-lfchen Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm pretty sure non-warping rotation is a saner default, the documentation should be updated accordingly

cr.scale(scale_x, scale_y);

// Children widget
if let Some(child) = &*self.content.borrow() {
Expand Down
4 changes: 4 additions & 0 deletions crates/eww/src/widgets/widget_definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,10 @@ fn build_transform(bargs: &mut BuilderArgs) -> Result<Transform> {
def_widget!(bargs, _g, w, {
// @prop rotate - the percentage to rotate
prop(rotate: as_f64) { w.set_property("rotate", rotate); },
// @prop transform-origin-x - x coordinate of origin of transformation (px or %)
prop(transform_origin_x: as_string) { w.set_property("transform-origin-x", transform_origin_x) },
// @prop transform-origin-y - y coordinate of origin of transformation (px or %)
prop(transform_origin_y: as_string) { w.set_property("transform-origin-y", transform_origin_y) },
// @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 %)
Expand Down
Loading