Skip to content

Commit 886a208

Browse files
yuraizmelix99
authored andcommitted
Add snowflakes to headerbar at the holiday season
1 parent 2ff6472 commit 886a208

6 files changed

Lines changed: 232 additions & 52 deletions

File tree

data/resources/ui/content-chat-history.ui

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,31 @@
55
<object class="GtkBox">
66
<property name="orientation">vertical</property>
77
<child>
8-
<object class="AdwHeaderBar">
9-
<property name="show-start-title-buttons" bind-source="ContentChatHistory" bind-property="compact" bind-flags="sync-create"/>
10-
<child type="start">
11-
<object class="GtkButton">
8+
<object class="GtkOverlay">
9+
<child>
10+
<object class="AdwHeaderBar">
11+
<property name="show-start-title-buttons" bind-source="ContentChatHistory" bind-property="compact" bind-flags="sync-create"/>
12+
<child type="start">
13+
<object class="GtkButton">
1214
<property name="visible" bind-source="ContentChatHistory" bind-property="compact" bind-flags="sync-create"/>
1315
<property name="icon-name">go-previous-symbolic</property>
14-
<property name="action-name">content.go-back</property>
16+
<property name="action-name">content.go-back</property>
17+
</object>
18+
</child>
19+
<child type="title">
20+
<object class="AdwWindowTitle" id="window_title"/>
21+
</child>
22+
<child type="end">
23+
<object class="GtkMenuButton">
24+
<property name="icon-name">view-more-symbolic</property>
25+
<property name="menu-model">chat-menu-model</property>
26+
</object>
27+
</child>
1528
</object>
1629
</child>
17-
<child type="title">
18-
<object class="AdwWindowTitle" id="window_title"/>
19-
</child>
20-
<child type="end">
21-
<object class="GtkMenuButton">
22-
<property name="icon-name">view-more-symbolic</property>
23-
<property name="menu-model">chat-menu-model</property>
30+
<child type="overlay">
31+
<object class="ComponentsSnow" id="snow">
32+
<property name="sensitive">False</property>
2433
</object>
2534
</child>
2635
</object>

data/resources/ui/content.ui

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,19 @@
99
<object class="GtkBox" id="unselected_chat">
1010
<property name="orientation">vertical</property>
1111
<child>
12-
<object class="AdwHeaderBar">
13-
<property name="show-start-title-buttons">False</property>
14-
<child type="title">
15-
<object class="AdwWindowTitle"/>
12+
<object class="GtkOverlay">
13+
<child>
14+
<object class="AdwHeaderBar">
15+
<property name="show-start-title-buttons">False</property>
16+
<child type="title">
17+
<object class="AdwWindowTitle"/>
18+
</child>
19+
</object>
20+
</child>
21+
<child type="overlay">
22+
<object class="ComponentsSnow" id="snow">
23+
<property name="sensitive">False</property>
24+
</object>
1625
</child>
1726
</object>
1827
</child>
@@ -22,20 +31,20 @@
2231
<property name="icon-name">user-available-symbolic</property>
2332
<property name="title" translatable="yes">No Chat Selected</property>
2433
<property name="description" translatable="yes">Select a chat to start messaging.</property>
34+
</object>
35+
</child>
2536
</object>
2637
</child>
27-
</object>
28-
</child>
2938
<child>
3039
<object class="ContentChatHistory" id="chat_history">
3140
<binding name="compact">
3241
<lookup name="compact">Content</lookup>
33-
</binding>
42+
</binding>
3443
<binding name="chat">
3544
<lookup name="chat">Content</lookup>
36-
</binding>
37-
</object>
38-
</child>
45+
</binding>
46+
</object>
47+
</child>
3948
</object>
4049
</child>
4150
</template>

data/resources/ui/sidebar.ui

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,47 @@
3535
<object class="GtkBox" id="main_box">
3636
<property name="orientation">vertical</property>
3737
<child>
38-
<object class="AdwHeaderBar">
39-
<property name="show-end-title-buttons" bind-source="Sidebar" bind-property="compact" bind-flags="sync-create"/>
40-
<child type="start">
41-
<object class="GtkMenuButton">
42-
<property name="popover">
43-
<object class="SessionSwitcher" id="session_switcher"/>
44-
</property>
45-
<style>
46-
<class name="image-button"/>
47-
</style>
48-
<property name="child">
49-
<object class="ComponentsAvatar">
50-
<property name="size">24</property>
51-
<binding name="item">
52-
<lookup name="me" type="Session">
53-
<lookup name="session">Sidebar</lookup>
54-
</lookup>
55-
</binding>
38+
<object class="GtkOverlay">
39+
<child>
40+
<object class="AdwHeaderBar">
41+
<property name="show-end-title-buttons" bind-source="Sidebar" bind-property="compact" bind-flags="sync-create"/>
42+
<child type="start">
43+
<object class="GtkMenuButton">
44+
<property name="popover">
45+
<object class="SessionSwitcher" id="session_switcher"/>
46+
</property>
47+
<style>
48+
<class name="image-button"/>
49+
</style>
50+
<property name="child">
51+
<object class="ComponentsAvatar">
52+
<property name="size">24</property>
53+
<binding name="item">
54+
<lookup name="me" type="Session">
55+
<lookup name="session">Sidebar</lookup>
56+
</lookup>
57+
</binding>
58+
</object>
59+
</property>
5660
</object>
57-
</property>
58-
</object>
59-
</child>
60-
<child type="end">
61-
<object class="GtkMenuButton">
62-
<property name="icon-name">open-menu-symbolic</property>
63-
<property name="menu-model">primary_menu</property>
61+
</child>
62+
<child type="end">
63+
<object class="GtkMenuButton">
64+
<property name="icon-name">open-menu-symbolic</property>
65+
<property name="menu-model">primary_menu</property>
66+
</object>
67+
</child>
68+
<child type="end">
69+
<object class="GtkButton">
70+
<property name="action-name">sidebar.start-search</property>
71+
<property name="icon-name">system-search-symbolic</property>
72+
</object>
73+
</child>
6474
</object>
6575
</child>
66-
<child type="end">
67-
<object class="GtkButton">
68-
<property name="action-name">sidebar.start-search</property>
69-
<property name="icon-name">system-search-symbolic</property>
76+
<child type="overlay">
77+
<object class="ComponentsSnow" id="snow">
78+
<property name="sensitive">False</property>
7079
</object>
7180
</child>
7281
</object>

src/session/components/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod avatar;
22
mod message_entry;
3+
mod snow;
34

45
pub(crate) use self::avatar::Avatar;
56
pub(crate) use self::message_entry::MessageEntry;
7+
pub(crate) use self::snow::Snow;

src/session/components/snow.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use gtk::prelude::*;
2+
use gtk::subclass::prelude::*;
3+
use gtk::{glib, gsk};
4+
5+
const SNOW_SHADER: &[u8] = r#"
6+
uniform float u_time;
7+
uniform vec4 u_color;
8+
9+
float random(vec2 co) {
10+
return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 43758.5453);
11+
}
12+
13+
float map(vec2 p) {
14+
float w = 0.05;
15+
p.y -= u_time * 0.05;
16+
vec2 idx = floor(p / w * 0.5 + 0.5);
17+
p.x += cos(u_time * random(idx)) * 0.01;
18+
float r = sin(random(idx)) * 0.005 + 0.001;
19+
p = mod(p + w, w * 2.) - w;
20+
float d = length(p - 0.03 * vec2(cos(idx.y), cos(idx.x))) - r;
21+
float color = d < 0.0 ? 0.5 : 0.0;
22+
color *= (sin(u_time * random(idx)) + 1.0) / 2.0;
23+
return color;
24+
}
25+
26+
void mainImage(out vec4 fragColor,
27+
in vec2 fragCoord,
28+
in vec2 resolution,
29+
in vec2 uv) {
30+
31+
float sum = 0;
32+
// anti-aliasing
33+
for (float i = -0.5; i < 1.5; i += 0.2) {
34+
for (float j = -0.5; j < 1.5; j += 0.2) {
35+
vec2 uv2 = (fragCoord.xy + vec2(i, j)) / resolution.yy * 0.2;
36+
sum += map(uv2);
37+
}
38+
}
39+
sum /= 25;
40+
41+
fragColor = u_color * sum;
42+
}
43+
"#
44+
.as_bytes();
45+
46+
mod imp {
47+
use super::*;
48+
use gtk::graphene;
49+
use std::cell::{Cell, RefCell};
50+
51+
#[derive(Default)]
52+
pub struct Snow {
53+
pub(super) snow_shader: RefCell<Option<gsk::GLShader>>,
54+
pub(super) time_start: Cell<i64>,
55+
}
56+
57+
#[glib::object_subclass]
58+
impl ObjectSubclass for Snow {
59+
const NAME: &'static str = "ComponentsSnow";
60+
type Type = super::Snow;
61+
type ParentType = gtk::Widget;
62+
}
63+
64+
impl ObjectImpl for Snow {
65+
fn constructed(&self) {
66+
if super::Snow::correct_date() {
67+
self.obj().add_tick_callback(|widget, _clock| {
68+
widget.queue_draw();
69+
glib::Continue(true)
70+
});
71+
} else {
72+
self.obj().set_visible(false);
73+
}
74+
}
75+
}
76+
77+
impl WidgetImpl for Snow {
78+
fn snapshot(&self, snapshot: &gtk::Snapshot) {
79+
let widget = self.obj();
80+
widget.ensure_shader();
81+
82+
if let Some(shader) = &*self.snow_shader.borrow() {
83+
let frame_time = widget.frame_clock().unwrap().frame_time();
84+
let time = (frame_time - self.time_start.get()) as f32 / 1000000.0;
85+
86+
let color = self.obj().style_context().color();
87+
let color =
88+
graphene::Vec4::new(color.red(), color.green(), color.blue(), color.alpha());
89+
let args_builder = gsk::ShaderArgsBuilder::new(shader, None);
90+
91+
args_builder.set_float(0, time);
92+
args_builder.set_vec4(1, &color);
93+
94+
let wigth = widget.width() as f32;
95+
let height = widget.height() as f32;
96+
let bounds = graphene::Rect::new(0.0, 0.0, wigth, height);
97+
98+
snapshot.push_gl_shader(shader, &bounds, &args_builder.to_args());
99+
snapshot.pop();
100+
}
101+
}
102+
}
103+
}
104+
105+
glib::wrapper! {
106+
pub struct Snow(ObjectSubclass<imp::Snow>)
107+
@extends gtk::Widget;
108+
}
109+
110+
impl Snow {
111+
fn correct_date() -> bool {
112+
match glib::DateTime::now_local() {
113+
Ok(date_time) => {
114+
let month = date_time.month();
115+
let day = date_time.day_of_month();
116+
match month {
117+
12 => day >= 24,
118+
1 => day <= 5,
119+
_ => false,
120+
}
121+
}
122+
Err(_) => false,
123+
}
124+
}
125+
126+
fn ensure_shader(&self) {
127+
let imp = self.imp();
128+
if imp.snow_shader.borrow().is_none() {
129+
let renderer = self.native().unwrap().renderer();
130+
131+
let shader = {
132+
let bytes = glib::Bytes::from_static(SNOW_SHADER);
133+
let shader = gsk::GLShader::from_bytes(&bytes);
134+
match shader.compile(&renderer) {
135+
Err(e) => {
136+
// That shader isn't important
137+
log::error!("Couldn't compile snow shader: {}", e);
138+
self.set_visible(false);
139+
None
140+
}
141+
Ok(_) => Some(shader),
142+
}
143+
};
144+
145+
imp.snow_shader.replace(shader);
146+
imp.time_start.set(self.frame_clock().unwrap().frame_time());
147+
}
148+
}
149+
}

src/session/sidebar/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod imp {
2828
use once_cell::unsync::OnceCell;
2929
use std::cell::{Cell, RefCell};
3030

31-
use crate::session::components::Avatar as ComponentsAvatar;
31+
use crate::session::components::{Avatar as ComponentsAvatar, Snow as ComponentsSnow};
3232

3333
#[derive(Debug, Default, CompositeTemplate)]
3434
#[template(resource = "/com/github/melix99/telegrand/ui/sidebar.ui")]
@@ -39,6 +39,8 @@ mod imp {
3939
pub(super) session: RefCell<Option<Session>>,
4040
pub(super) row_menu: OnceCell<gtk::PopoverMenu>,
4141
#[template_child]
42+
pub(super) snow: TemplateChild<ComponentsSnow>,
43+
#[template_child]
4244
pub(super) stack: TemplateChild<gtk::Stack>,
4345
#[template_child]
4446
pub(super) main_box: TemplateChild<gtk::Box>,

0 commit comments

Comments
 (0)