Skip to content

Commit ff0ab78

Browse files
committed
stickers: Add vector path
1 parent fcf312f commit ff0ab78

File tree

3 files changed

+99
-4
lines changed

3 files changed

+99
-4
lines changed

src/components/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ mod avatar;
22
mod message_entry;
33
mod snow;
44
mod sticker;
5+
mod vector_path;
56

67
pub(crate) use self::avatar::Avatar;
78
pub(crate) use self::message_entry::MessageEntry;
89
pub(crate) use self::snow::Snow;
910
pub(crate) use self::sticker::Sticker;
11+
pub(crate) use self::vector_path::VectorPath;

src/components/sticker.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use gtk::{gio, glib};
66
use tdlib::enums::StickerFormat;
77
use tdlib::types::Sticker as TdSticker;
88

9+
use super::VectorPath;
910
use crate::session::Session;
1011
use crate::utils::{decode_image_from_path, spawn};
1112

@@ -52,11 +53,15 @@ mod imp {
5253
}
5354

5455
impl WidgetImpl for Sticker {
55-
fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
56+
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
5657
let size = self.longer_side_size.get();
5758
let aspect_ratio = self.aspect_ratio.get();
5859

59-
let min_size = 1;
60+
let min_size = if let Some(child) = &*self.child.borrow_mut() {
61+
child.measure(orientation, for_size).0
62+
} else {
63+
1
64+
};
6065

6166
let size = if let gtk::Orientation::Horizontal = orientation {
6267
if aspect_ratio >= 1.0 {
@@ -103,8 +108,7 @@ impl Sticker {
103108
return;
104109
}
105110

106-
// TODO: draw sticker outline with cairo
107-
self.set_child(None);
111+
self.set_child(Some(VectorPath::new(&sticker.outline).upcast()));
108112

109113
let aspect_ratio = sticker.width as f64 / sticker.height as f64;
110114
imp.aspect_ratio.set(aspect_ratio);

src/components/vector_path.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use gtk::prelude::*;
2+
use gtk::subclass::prelude::*;
3+
use gtk::{glib, graphene, gsk};
4+
5+
use tdlib::types::ClosedVectorPath;
6+
7+
mod imp {
8+
use super::*;
9+
use std::cell::RefCell;
10+
11+
#[derive(Default)]
12+
pub(crate) struct VectorPath {
13+
pub(super) node: RefCell<Option<gsk::RenderNode>>,
14+
}
15+
16+
#[glib::object_subclass]
17+
impl ObjectSubclass for VectorPath {
18+
const NAME: &'static str = "ComponentsVectorPath";
19+
type Type = super::VectorPath;
20+
type ParentType = gtk::Widget;
21+
22+
fn class_init(klass: &mut Self::Class) {
23+
klass.set_css_name("vectorpath");
24+
}
25+
}
26+
27+
impl ObjectImpl for VectorPath {}
28+
29+
impl WidgetImpl for VectorPath {
30+
fn snapshot(&self, snapshot: &gtk::Snapshot) {
31+
let widget = self.obj();
32+
33+
let factor = (widget.width() as f32).max(widget.height() as f32) / 512.0;
34+
snapshot.scale(factor, factor);
35+
36+
if let Some(node) = &*self.node.borrow() {
37+
snapshot.append_node(node);
38+
}
39+
}
40+
}
41+
}
42+
43+
glib::wrapper! {
44+
pub(crate) struct VectorPath(ObjectSubclass<imp::VectorPath>)
45+
@extends gtk::Widget;
46+
}
47+
48+
impl VectorPath {
49+
pub fn new(outline: &[ClosedVectorPath]) -> Self {
50+
let obj: Self = glib::Object::new();
51+
obj.imp().node.replace(Self::path_node(outline));
52+
obj
53+
}
54+
55+
fn path_node(outline: &[ClosedVectorPath]) -> Option<gsk::RenderNode> {
56+
use tdlib::enums::VectorPathCommand::{CubicBezierCurve, Line};
57+
use tdlib::types::VectorPathCommandCubicBezierCurve as Curve;
58+
59+
let snapshot = gtk::Snapshot::new();
60+
let context = snapshot.append_cairo(&graphene::Rect::new(0.0, 0.0, 512.0, 512.0));
61+
62+
context.set_source_rgba(0.5, 0.5, 0.5, 0.4);
63+
64+
for closed_path in outline {
65+
context.new_sub_path();
66+
for command in &closed_path.commands {
67+
match command {
68+
Line(line) => {
69+
let e = &line.end_point;
70+
context.line_to(e.x, e.y);
71+
}
72+
CubicBezierCurve(curve) => {
73+
let Curve {
74+
start_control_point: sc,
75+
end_control_point: ec,
76+
end_point: e,
77+
} = curve;
78+
79+
context.curve_to(sc.x, sc.y, ec.x, ec.y, e.x, e.y);
80+
}
81+
}
82+
}
83+
context.close_path();
84+
}
85+
_ = context.fill();
86+
87+
snapshot.to_node()
88+
}
89+
}

0 commit comments

Comments
 (0)