Skip to content

Commit

Permalink
feat(message-document): Use mask for status indicator
Browse files Browse the repository at this point in the history
With mask it correctly matches message bubble background
  • Loading branch information
yuraiz committed Jun 14, 2023
1 parent d9061a5 commit 786c4fe
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 70 deletions.
4 changes: 4 additions & 0 deletions data/resources/style-dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ messagebubble.outgoing {
background-color: #2c52ac;
}

messagebubble.document.outgoing .file > overlay > statusindicator {
color: white;
}

.event-row {
background-color: alpha(#404040, 0.8);
}
Expand Down
36 changes: 12 additions & 24 deletions data/resources/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -196,32 +196,30 @@ messagebubble.document .file > overlay {
min-height: 40px;
}

messagebubble.document .file > overlay > image {
background: @accent_color;
color: @window_bg_color;
transition: opacity 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
messagebubble.document .file > overlay > statusindicator > image {
padding: 12px;
}

messagebubble.document .file > overlay > statusindicator {
color: @accent_color;
transition: opacity 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
border-radius: 9999px;
}

messagebubble.document.outgoing .file > overlay > image {
background: @accent_fg_color;
color: @accent_bg_color;
messagebubble.document.outgoing .file > overlay > statusindicator {
/* depends on bubble color */
color: #79c271;
}

messagebubble.document .file:hover > overlay > image {
messagebubble.document .file:hover > overlay > statusindicator {
opacity: 0.85;
}

messagebubble.document .file:active > overlay > image {
messagebubble.document .file:active > overlay > statusindicator {
opacity: 0.7;
}
messagebubble.document .file.with-thumbnail > overlay > image {
background-color: alpha(black, 0.6);
color: white;
}

messagebubble.document .file.with-thumbnail > overlay > image {
messagebubble.document .file.with-thumbnail > overlay > statusindicator {
background-color: alpha(black, 0.6);
color: white;
}
Expand Down Expand Up @@ -251,16 +249,6 @@ messagebubble.media.with-reply mediapicture {
margin-top: 3px;
}

messagebubble.document .file.with-thumbnail > overlay > image {
background-color: alpha(black, 0.6);
color: white;
}

messagebubble.document .file.with-thumbnail > overlay {
min-width: 60px;
min-height: 60px;
}

messagebubble.document .file.with-thumbnail > overlay > picture {
border-radius: 6px;
}
Expand Down
2 changes: 1 addition & 1 deletion data/resources/ui/content-message-document.blp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ template $MessageDocument : $MessageBase {
}

[overlay]
Image file_status_image {
$MessageDocumentStatusIndicator status_indicator {
halign: center;
valign: center;
}
Expand Down
34 changes: 34 additions & 0 deletions src/session/content/message_row/document/file_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use tdlib::types::File;
use FileStatus::*;

#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum FileStatus {
Downloading(f64),
Uploading(f64),
CanBeDownloaded,
Downloaded,
}

impl From<&File> for FileStatus {
fn from(file: &File) -> Self {
let local = &file.local;
let remote = &file.remote;

let size = file.size.max(file.expected_size) as u64;

if local.is_downloading_active {
let progress = local.downloaded_size as f64 / size as f64;
Downloading(progress)
} else if remote.is_uploading_active {
let progress = remote.uploaded_size as f64 / size as f64;
Uploading(progress)
} else if local.is_downloading_completed {
Downloaded
} else if local.can_be_downloaded {
CanBeDownloaded
} else {
dbg!(file);
unimplemented!("unknown file status");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::cell::RefCell;
mod file_status;
mod status_indicator;

use file_status::FileStatus;
use file_status::FileStatus::*;
use glib::clone;
use gtk::gdk;
use gtk::gio;
Expand All @@ -8,6 +12,7 @@ use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::CompositeTemplate;
use once_cell::sync::Lazy;
use status_indicator::StatusIndicator;
use tdlib::enums::MessageContent;
use tdlib::types::File;

Expand Down Expand Up @@ -39,7 +44,7 @@ mod imp {
#[template_child]
pub(super) file_thumbnail_picture: TemplateChild<gtk::Picture>,
#[template_child]
pub(super) file_status_image: TemplateChild<gtk::Image>,
pub(super) status_indicator: TemplateChild<StatusIndicator>,
#[template_child]
pub(super) file_name_label: TemplateChild<gtk::Label>,
#[template_child]
Expand Down Expand Up @@ -128,39 +133,6 @@ impl MessageBaseExt for MessageDocument {
}
}

#[derive(PartialEq)]
enum FileStatus {
Downloading(f64),
Uploading(f64),
CanBeDownloaded,
Downloaded,
}
use FileStatus::*;

impl From<&File> for FileStatus {
fn from(file: &File) -> Self {
let local = &file.local;
let remote = &file.remote;

let size = file.size.max(file.expected_size) as u64;

if local.is_downloading_active {
let progress = local.downloaded_size as f64 / size as f64;
Downloading(progress)
} else if remote.is_uploading_active {
let progress = remote.uploaded_size as f64 / size as f64;
Uploading(progress)
} else if local.is_downloading_completed {
Downloaded
} else if local.can_be_downloaded {
CanBeDownloaded
} else {
dbg!(file);
unimplemented!("unknown file status");
}
}
}

impl MessageDocument {
fn update_document(&self, message: &Message) {
if let MessageContent::MessageDocument(data) = message.content().0 {
Expand All @@ -183,34 +155,34 @@ impl MessageDocument {

let size = file.size.max(file.expected_size) as u64;

self.update_size_label(&status, size);
self.update_button(file, session, &status);
self.update_size_label(status, size);
self.update_button(file, session, status);

status
}

fn update_button(&self, file: File, session: Session, status: &FileStatus) {
fn update_button(&self, file: File, session: Session, status: FileStatus) {
let imp = self.imp();
let click = &*imp.click;
let image = &*imp.file_status_image;
let indicator = &*imp.status_indicator;
let file_id = file.id;

let handler_id = match *status {
let handler_id = match status {
Downloading(_progress) | Uploading(_progress) => {
return;
// Show loading indicator
}
CanBeDownloaded => {
// Download file
image.set_icon_name(Some("document-save-symbolic"));
indicator.set_status(CanBeDownloaded);
click.connect_released(clone!(@weak self as obj, @weak session => move |click, _, _, _| {
// TODO: Fix bug mentioned here
// https://github.com/paper-plane-developers/paper-plane/pull/372#discussion_r968841370
session.download_file_with_updates(file_id, clone!(@weak obj, @weak session => move |file| {
obj.update_status(file, session);
}));

obj.imp().file_status_image.set_icon_name(Some("media-playback-stop-symbolic"));
obj.imp().status_indicator.set_status(Downloading(0.0));
let handler_id = click.connect_released(clone!(@weak session => move |_, _, _, _| {
session.cancel_download_file(file_id);
}));
Expand All @@ -221,9 +193,9 @@ impl MessageDocument {
}
Downloaded => {
// Open file
image.set_icon_name(Some("folder-documents-symbolic"));
indicator.set_status(Downloaded);
if imp.file_thumbnail_picture.file().is_some() {
image.set_visible(false);
indicator.set_visible(false);
}
let gio_file = gio::File::for_path(&file.local.path);
click.connect_released(move |_, _, _, _| {
Expand All @@ -242,7 +214,7 @@ impl MessageDocument {
}
}

fn update_size_label(&self, status: &FileStatus, size: u64) {
fn update_size_label(&self, status: FileStatus, size: u64) {
let size_label = &self.imp().file_size_label;

match status {
Expand All @@ -262,6 +234,7 @@ impl MessageDocument {
if let MessageContent::MessageDocument(data) = message.content().0 {
let imp = self.imp();
if let Some(thumbnail) = data.document.thumbnail {
imp.status_indicator.set_masked(false);
imp.file_thumbnail_picture.set_visible(true);
imp.file_box.add_css_class("with-thumbnail");
if thumbnail.file.local.is_downloading_completed {
Expand All @@ -288,7 +261,8 @@ impl MessageDocument {
}));
}
} else {
imp.file_thumbnail_picture.set_visible(true);
imp.status_indicator.set_masked(true);
imp.file_thumbnail_picture.set_visible(false);
imp.file_thumbnail_picture
.set_paintable(gdk::Paintable::NONE);
}
Expand Down
117 changes: 117 additions & 0 deletions src/session/content/message_row/document/status_indicator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::cell::Cell;

use adw::prelude::*;
use adw::subclass::prelude::*;
use gtk::glib;
use gtk::gsk;
use gtk::CompositeTemplate;

use super::file_status::FileStatus;

mod imp {
use super::*;

#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(string = r#"
template $MessageDocumentStatusIndicator {
layout-manager: BinLayout {};
overflow: hidden;
Image status_image {}
}
"#)]
#[properties(wrapper_type = super::StatusIndicator)]
pub(crate) struct StatusIndicator {
#[property(get, set = Self::set_masked, explicit_notify)]
pub(super) masked: Cell<bool>,
#[template_child]
pub(super) status_image: TemplateChild<gtk::Image>,
}

#[glib::object_subclass]
impl ObjectSubclass for StatusIndicator {
const NAME: &'static str = "MessageDocumentStatusIndicator";
type Type = super::StatusIndicator;
type ParentType = gtk::Widget;

fn class_init(klass: &mut Self::Class) {
klass.set_css_name("statusindicator");
klass.bind_template();
}

fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}

impl ObjectImpl for StatusIndicator {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}

fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
self.derived_set_property(id, value, pspec)
}

fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.derived_property(id, pspec)
}
}

impl WidgetImpl for StatusIndicator {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
if self.masked.get() {
let widget = self.obj();

snapshot.push_mask(gsk::MaskMode::InvertedAlpha);

self.parent_snapshot(snapshot);

snapshot.pop();

snapshot.append_color(
&widget.color(),
&gtk::graphene::Rect::new(
0.0,
0.0,
widget.width() as f32,
widget.height() as f32,
),
);

snapshot.pop();
} else {
self.parent_snapshot(snapshot);
}
}
}

impl BinImpl for StatusIndicator {}

impl StatusIndicator {
fn set_masked(&self, masked: bool) {
if self.masked.replace(masked) != masked {
let obj = self.obj();
obj.queue_draw();
obj.notify("masked")
}
}
}
}

glib::wrapper! {
pub(crate) struct StatusIndicator(ObjectSubclass<imp::StatusIndicator>)
@extends gtk::Widget;
}

impl StatusIndicator {
pub(crate) fn set_status(&self, status: FileStatus) {
let icon_name = match status {
FileStatus::Downloading(_) | FileStatus::Uploading(_) => "media-playback-stop-symbolic",
FileStatus::CanBeDownloaded => "document-save-symbolic",
FileStatus::Downloaded => "folder-documents-symbolic",
};

self.imp().status_image.set_icon_name(Some(icon_name));
}
}

0 comments on commit 786c4fe

Please sign in to comment.