Skip to content

Commit

Permalink
chat-history: Add 4-color gradient to messages
Browse files Browse the repository at this point in the history
  • Loading branch information
yuraiz committed May 20, 2023
1 parent 1cba840 commit 4852ddf
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 96 deletions.
146 changes: 103 additions & 43 deletions src/session/content/background.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::clone;
use gtk::{gdk, gio, glib, graphene, gsk};
use std::cell::{Cell, RefCell};

Expand Down Expand Up @@ -49,7 +50,9 @@ mod imp {

#[derive(Default)]
pub(crate) struct Background {
pub(super) gradient_texture: RefCell<Option<gdk::Texture>>,
pub(super) background_texture: RefCell<Option<gdk::Texture>>,
pub(super) message_texture: RefCell<Option<gdk::Texture>>,

pub(super) last_size: Cell<(f32, f32)>,

pub(super) shader: RefCell<Option<gsk::GLShader>>,
Expand All @@ -61,7 +64,8 @@ mod imp {

pub(super) dark: Cell<bool>,

pub(super) colors: RefCell<Vec<graphene::Vec3>>,
pub(super) bg_colors: RefCell<Vec<graphene::Vec3>>,
pub(super) message_colors: RefCell<Vec<graphene::Vec3>>,
}

#[glib::object_subclass]
Expand Down Expand Up @@ -102,7 +106,7 @@ mod imp {

let target = adw::CallbackAnimationTarget::new(clone!(@weak obj => move |progress| {
let imp = obj.imp();
imp.gradient_texture.take();
imp.background_texture.take();
let progress = progress as f32;
if progress >= 1.0 {
imp.progress.set(0.0);
Expand Down Expand Up @@ -171,18 +175,20 @@ mod imp {
size_changed: bool,
) {
if self.progress.get() == 0.0 {
let texture = match self.gradient_texture.take() {
Some(texture) if !size_changed => texture,
let texture = match self.background_texture.take() {
Some(texture) if !size_changed => texture.clone(),
_ => {
let renderer = self.obj().native().unwrap().renderer();
renderer.render_texture(self.gradient_shader_node(bounds), Some(bounds))
self.render_textures(bounds);
self.background_texture.take().unwrap()
}
};

snapshot.append_texture(&texture, bounds);
self.gradient_texture.replace(Some(texture));
self.background_texture.replace(Some(texture));
} else {
snapshot.append_node(self.gradient_shader_node(bounds));
self.render_textures(bounds);
let texture = self.background_texture.borrow().as_ref().unwrap().clone();
snapshot.append_texture(&texture, bounds);
}
}

Expand Down Expand Up @@ -220,7 +226,24 @@ mod imp {
}
}

fn gradient_shader_node(&self, bounds: &graphene::Rect) -> gsk::GLShaderNode {
fn render_textures(&self, bounds: &graphene::Rect) {
let colors = [self.bg_colors.borrow(), self.message_colors.borrow()];

let renderer = self.obj().native().unwrap().renderer();

let mut textures = colors.into_iter().map(|color| {
renderer.render_texture(self.gradient_shader_node(bounds, &color), Some(bounds))
});

self.background_texture.replace(textures.next());
self.message_texture.replace(textures.next());
}

fn gradient_shader_node(
&self,
bounds: &graphene::Rect,
colors: &[graphene::Vec3],
) -> gsk::GLShaderNode {
let Some(gradient_shader) = &*self.shader.borrow() else {
unreachable!()
};
Expand All @@ -230,10 +253,8 @@ mod imp {
let progress = self.progress.get();
let phase = self.phase.get() as usize;

let colors = self.colors.borrow();

let &[c1, c2, c3, c4] = &colors[..] else {
unimplemented!("Unexpected color count");
let &[c1, c2, c3, c4] = colors else {
unimplemented!("Unexpected color count")
};

args_builder.set_vec3(0, &c1);
Expand Down Expand Up @@ -299,38 +320,19 @@ impl Background {

imp.dark.set(background.is_dark);

let fill = match background.r#type {
let bg_fill = match background.r#type {
tdlib::enums::BackgroundType::Pattern(pattern) => pattern.fill,
tdlib::enums::BackgroundType::Fill(fill) => fill.fill,
tdlib::enums::BackgroundType::Wallpaper(_) => {
unimplemented!("Wallpaper chat background")
}
};

match fill {
tdlib::enums::BackgroundFill::FreeformGradient(gradient) => {
if gradient.colors.len() != 4 {
unimplemented!("Unsupported gradient colors count");
}

let colors = gradient
.colors
.into_iter()
.map(|int_color| {
let r = (int_color >> 16) & 0xFF;
let g = (int_color >> 8) & 0xFF;
let b = int_color & 0xFF;
imp.bg_colors.replace(fill_colors(bg_fill));
imp.message_colors
.replace(fill_colors(theme.outgoing_message_fill));

graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
})
.collect();

imp.colors.replace(colors);
}
_ => unimplemented!("Background fill"),
}

imp.gradient_texture.take();
imp.background_texture.take();
self.queue_draw();
}

Expand All @@ -343,6 +345,29 @@ impl Background {
}
}

pub fn subscribe_to_redraw(&self, child: &gtk::Widget) {
let animation = self.imp().animation.get().unwrap();
animation.connect_value_notify(clone!(@weak child => move |_| child.queue_draw()));
}

pub fn bg_texture(&self) -> gdk::Texture {
self.imp()
.background_texture
.borrow()
.as_ref()
.unwrap()
.clone()
}

pub fn message_texture(&self) -> gdk::Texture {
self.imp()
.message_texture
.borrow()
.as_ref()
.unwrap()
.clone()
}

fn ensure_shader(&self) {
let imp = self.imp();
if imp.shader.borrow().is_none() {
Expand Down Expand Up @@ -370,8 +395,31 @@ impl Default for Background {
}
}

fn fill_colors(fill: tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
match fill {
tdlib::enums::BackgroundFill::FreeformGradient(gradient) if gradient.colors.len() == 4 => {
gradient
.colors
.into_iter()
.map(|int_color| {
let r = (int_color >> 16) & 0xFF;
let g = (int_color >> 8) & 0xFF;
let b = int_color & 0xFF;

graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
})
.collect()
}
_ => unimplemented!("Unsupported background fill: {fill:?}"),
}
}

fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
fn theme(dark: bool, colors: Vec<i32>) -> tdlib::types::ThemeSettings {
fn theme(
dark: bool,
bg_colors: Vec<i32>,
message_colors: Vec<i32>,
) -> tdlib::types::ThemeSettings {
use tdlib::enums::BackgroundFill::*;
use tdlib::enums::BackgroundType::Fill;
use tdlib::types::*;
Expand All @@ -381,7 +429,7 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
is_default: true,
is_dark: dark,
r#type: Fill(BackgroundTypeFill {
fill: FreeformGradient(BackgroundFillFreeformGradient { colors }),
fill: FreeformGradient(BackgroundFillFreeformGradient { colors: bg_colors }),
}),
id: 0,
name: String::new(),
Expand All @@ -390,13 +438,25 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
accent_color: 0,
animate_outgoing_message_fill: false,
outgoing_message_accent_color: 0,
outgoing_message_fill: Solid(BackgroundFillSolid { color: 0 }),
outgoing_message_fill: FreeformGradient(BackgroundFillFreeformGradient {
colors: message_colors,
}),
}
}

// tr tl bl br

if dark {
theme(dark, vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5])
theme(
dark,
vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5],
vec![0xfc27a6, 0xff9201, 0x7827ff, 0x554efe],
)
} else {
theme(dark, vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7])
theme(
dark,
vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7],
vec![0xddffdf, 0xfff0dd, 0xffddfc, 0xddecff],
)
}
}
77 changes: 24 additions & 53 deletions src/session/content/message_row/bubble.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use adw::prelude::*;
use glib::clone;
use gtk::subclass::prelude::*;
use gtk::{gdk, glib, graphene, gsk, CompositeTemplate};
use gtk::{glib, CompositeTemplate};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use crate::session::content::background::Background;
use crate::session::content::message_row::{MessageIndicators, MessageLabel, MessageReply};
use crate::tdlib::{Chat, ChatType, Message, MessageSender, SponsoredMessage};

Expand Down Expand Up @@ -64,6 +65,7 @@ mod imp {
pub(super) sender_color_class: RefCell<Option<String>>,
pub(super) sender_binding: RefCell<Option<gtk::ExpressionWatch>>,
pub(super) parent_list_view: RefCell<glib::WeakRef<gtk::ListView>>,
pub(super) parent_background: RefCell<glib::WeakRef<Background>>,
#[template_child]
pub(super) overlay: TemplateChild<gtk::Overlay>,
#[template_child]
Expand Down Expand Up @@ -136,27 +138,28 @@ mod imp {
widget.queue_draw();
}));
}

if let Some(background) = widget.parent_background() {
self.parent_background.replace(background.downgrade());
background.subscribe_to_redraw(widget.upcast_ref());
}
}

fn snapshot(&self, snapshot: &gtk::Snapshot) {
let widget = self.obj();
if widget.has_css_class("outgoing") {
let width = widget.width() as f32;
let height = widget.height() as f32;

let bounds = graphene::Rect::new(0.0, 0.0, width, height);
let gradient_bounds = widget.gradient_bounds();
let [first, second] = widget.linear_gradient_colors();

snapshot.append_linear_gradient(
&bounds,
&graphene::Point::new(0.0, gradient_bounds.y()),
&graphene::Point::new(0.0, gradient_bounds.height()),
&[
gsk::ColorStop::new(0.0, first),
gsk::ColorStop::new(1.0, second),
],
);

if let Some(background) = self.parent_background.borrow().upgrade() {
if !background.has_css_class("fallback") {
let gradient_bounds = background.compute_bounds(self.obj().as_ref()).unwrap();

if widget.has_css_class("outgoing") {
snapshot.append_texture(&background.message_texture(), &gradient_bounds);
} else {
snapshot.push_opacity(0.1);
snapshot.append_texture(&background.bg_texture(), &gradient_bounds);
snapshot.pop();
};
}
}

self.parent_snapshot(snapshot);
Expand Down Expand Up @@ -353,42 +356,10 @@ impl MessageBubble {
}

fn parent_list_view(&self) -> Option<gtk::ListView> {
let mut parent = self.parent()?;
loop {
match parent.downcast() {
Ok(list_view) => return Some(list_view),
Err(not_list_view) => parent = not_list_view.parent()?,
}
}
self.ancestor(gtk::ListView::static_type())?.downcast().ok()
}

fn gradient_bounds(&self) -> graphene::Rect {
if let Some(view) = self.imp().parent_list_view.borrow().upgrade() {
let view_bounds = view.compute_bounds(self.imp().obj().as_ref()).unwrap();

graphene::Rect::new(
view_bounds.x(),
view_bounds.y(),
view_bounds.width() + view_bounds.x(),
view_bounds.height() + view_bounds.y(),
)
} else {
panic!("can't get parent ListView");
}
}

fn linear_gradient_colors(&self) -> [gdk::RGBA; 2] {
// default colors from iOS
if !adw::StyleManager::default().is_dark() {
[
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
]
} else {
[
gdk::RGBA::new(0.21960784, 0.32156864, 0.89411765, 1.0),
gdk::RGBA::new(0.63529414, 0.3372549, 0.58431375, 1.0),
]
}
fn parent_background(&self) -> Option<Background> {
self.ancestor(Background::static_type())?.downcast().ok()
}
}

0 comments on commit 4852ddf

Please sign in to comment.