Clean up file widget code

This commit is contained in:
fiaxh 2020-04-10 21:08:23 +02:00
parent bd8063284c
commit ff4403fb21
4 changed files with 303 additions and 211 deletions

View file

@ -50,6 +50,7 @@ set(RESOURCE_LIST
conversation_list_titlebar_csd.ui conversation_list_titlebar_csd.ui
conversation_row.ui conversation_row.ui
conversation_view.ui conversation_view.ui
file_default_widget.ui
emojichooser.ui emojichooser.ui
global_search.ui global_search.ui
conversation_content_view/image_toolbar.ui conversation_content_view/image_toolbar.ui
@ -140,6 +141,7 @@ SOURCES
src/ui/conversation_content_view/conversation_item_skeleton.vala src/ui/conversation_content_view/conversation_item_skeleton.vala
src/ui/conversation_content_view/conversation_view.vala src/ui/conversation_content_view/conversation_view.vala
src/ui/conversation_content_view/date_separator_populator.vala src/ui/conversation_content_view/date_separator_populator.vala
src/ui/conversation_content_view/file_default_widget.vala
src/ui/conversation_content_view/file_widget.vala src/ui/conversation_content_view/file_widget.vala
src/ui/conversation_content_view/message_widget.vala src/ui/conversation_content_view/message_widget.vala
src/ui/conversation_content_view/subscription_notification.vala src/ui/conversation_content_view/subscription_notification.vala

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="DinoUiFileDefaultWidget" parent="GtkEventBox">
<property name="halign">start</property>
<property name="visible">True</property>
<style>
<class name="file-box-outer"/>
</style>
<child>
<object class="DinoUiSizingBin">
<property name="target-width">500</property>
<property name="max-width">500</property>
<property name="hexpand">True</property>
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="hexpand">true</property>
<property name="spacing">10</property>
<property name="visible">True</property>
<style>
<class name="file-box"/>
</style>
<child>
<object class="GtkEventBox" id="stack_event_box">
<property name="visible">True</property>
<child>
<object class="GtkStack" id="image_stack">
<property name="transition-type">crossfade</property>
<property name="transition-duration">50</property>
<property name="valign">center</property>
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="icon-name">dino-file-download-symbolic</property>
<property name="icon-size">5</property>
<property name="opacity">0.7</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">download_image</property>
</packing>
</child>
<child>
<object class="GtkImage" id="content_type_image">
<property name="icon-size">5</property>
<property name="opacity">0.7</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">content_type_image</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
</object>
<packing>
<property name="name">spinner</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">True</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="ellipsize">middle</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="mime_label">
<property name="xalign">0</property>
<property name="yalign">1</property>
<property name="visible">True</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View file

@ -0,0 +1,127 @@
using Gee;
using Gdk;
using Gtk;
using Dino.Entities;
namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")]
public class FileDefaultWidget : EventBox {
[GtkChild] public Stack image_stack;
[GtkChild] public Label name_label;
[GtkChild] public Label mime_label;
[GtkChild] public Image content_type_image;
[GtkChild] public Spinner spinner;
[GtkChild] public EventBox stack_event_box;
private FileTransfer.State state;
private bool pointer_inside = false;
public FileDefaultWidget() {
this.enter_notify_event.connect(on_pointer_entered);
this.leave_notify_event.connect(on_pointer_left);
stack_event_box.enter_notify_event.connect((event) => { pointer_inside = true; return false; });
mime_label.enter_notify_event.connect((event) => { pointer_inside = true; return false; });
stack_event_box.leave_notify_event.connect((event) => { pointer_inside = true; return false; });
mime_label.leave_notify_event.connect((event) => { pointer_inside = true; return false; });
}
public void update_file_info(string? mime_type, FileTransfer.State state, int size) {
this.state = state;
spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap
content_type_image.icon_name = get_file_icon_name(mime_type);
string? mime_description = mime_type != null ? ContentType.get_description(mime_type) : null;
switch (state) {
case FileTransfer.State.COMPLETE:
mime_label.label = mime_description;
image_stack.set_visible_child_name("content_type_image");
break;
case FileTransfer.State.IN_PROGRESS:
mime_label.label = _("Downloading %s…").printf(get_size_string(size));
spinner.active = true;
image_stack.set_visible_child_name("spinner");
break;
case FileTransfer.State.NOT_STARTED:
if (mime_description != null) {
mime_label.label = _("%s offered: %s").printf(mime_description, get_size_string(size));
} else if (size != -1) {
mime_label.label = _("File offered: %s").printf(get_size_string(size));
} else {
mime_label.label = _("File offered");
}
image_stack.set_visible_child_name("content_type_image");
break;
case FileTransfer.State.FAILED:
mime_label.use_markup = true;
mime_label.label = "<span foreground=\"#f44336\">" + _("File transfer failed") + "</span>";
image_stack.set_visible_child_name("content_type_image");
break;
}
}
private bool on_pointer_entered(Gdk.EventCrossing event) {
pointer_inside = true;
Timeout.add(20, () => {
if (pointer_inside) {
event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.HAND2));
content_type_image.opacity = 0.7;
if (state == FileTransfer.State.NOT_STARTED) {
image_stack.set_visible_child_name("download_image");
}
}
return false;
});
return false;
}
private bool on_pointer_left(Gdk.EventCrossing event) {
pointer_inside = false;
Timeout.add(20, () => {
if (!pointer_inside) {
event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.XTERM));
content_type_image.opacity = 0.5;
if (state == FileTransfer.State.NOT_STARTED) {
image_stack.set_visible_child_name("content_type_image");
}
}
return false;
});
return false;
}
private static string get_file_icon_name(string? mime_type) {
if (mime_type == null) return "dino-file-symbolic";
string generic_icon_name = ContentType.get_generic_icon_name(mime_type) ?? "";
switch (generic_icon_name) {
case "audio-x-generic": return "dino-file-music-symbolic";
case "image-x-generic": return "dino-file-image-symbolic";
case "text-x-generic": return "dino-file-document-symbolic";
case "text-x-generic-template": return "dino-file-document-symbolic";
case "video-x-generic": return "dino-file-video-symbolic";
case "x-office-document": return "dino-file-document-symbolic";
case "x-office-spreadsheet": return "dino-file-table-symbolic";
default: return "dino-file-symbolic";
}
}
private static string get_size_string(int size) {
if (size < 1024) {
return @"$(size) B";
} else if (size < 1000 * 1000) {
return @"$(size / 1000) kB";
} else if (size < 1000 * 1000 * 1000) {
return @"$(size / 1000 / 1000) MB";
} else {
return @"$(size / 1000 / 1000 / 1000) GB";
}
}
}
}

View file

@ -39,18 +39,9 @@ public class FileWidget : Box {
private FileTransfer file_transfer; private FileTransfer file_transfer;
private State state; private State state;
// default box private FileDefaultWidgetController default_widget_controller;
private Box main_box;
private Image content_type_image;
private Image download_image;
private Spinner spinner;
private Label mime_label;
private Stack image_stack;
private Widget content; private Widget content;
private bool pointer_inside = false;
public FileWidget(StreamInteractor stream_interactor, FileTransfer file_transfer) { public FileWidget(StreamInteractor stream_interactor, FileTransfer file_transfer) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.file_transfer = file_transfer; this.file_transfer = file_transfer;
@ -61,32 +52,54 @@ public class FileWidget : Box {
Idle.add(() => { parent.queue_resize(); return false; }); Idle.add(() => { parent.queue_resize(); return false; });
} }
}); });
file_transfer.notify["state"].connect(update_widget_type);
file_transfer.notify["mime-type"].connect(update_widget_type);
} }
private async void load_widget() { private async void load_widget() {
if (show_image()) { if (show_image()) {
content = yield get_image_widget(file_transfer); content = yield get_image_widget(file_transfer.get_file(), file_transfer.file_name);
if (content != null) { if (content != null) {
this.state = State.IMAGE; this.state = State.IMAGE;
this.add(content); this.add(content);
return; return;
} }
} }
content = get_default_widget(file_transfer); FileDefaultWidget default_file_widget = new FileDefaultWidget() { visible=true };
default_widget_controller = new FileDefaultWidgetController(default_file_widget, file_transfer, stream_interactor);
content = default_file_widget;
this.state = State.DEFAULT; this.state = State.DEFAULT;
this.add(content); this.add(content);
} }
private async Widget? get_image_widget(FileTransfer file_transfer) { private async void update_widget_type() {
if (file_transfer.state == FileTransfer.State.COMPLETE && show_image() && state != State.IMAGE) {
this.remove(content);
this.add(yield get_image_widget(file_transfer.get_file(), file_transfer.file_name));
state = State.IMAGE;
return;
}
if (file_transfer.state == FileTransfer.State.FAILED && state == State.IMAGE) {
this.remove(content);
FileDefaultWidget default_file_widget = new FileDefaultWidget() { visible=true };
default_widget_controller = new FileDefaultWidgetController(default_file_widget, file_transfer, stream_interactor);
content = default_file_widget;
this.state = State.DEFAULT;
this.add(content);
}
}
public static async Widget? get_image_widget(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) {
// Load and prepare image in tread // Load and prepare image in tread
Thread<Image?> thread = new Thread<Image?> (null, () => { Thread<Image?> thread = new Thread<Image?> (null, () => {
ScalingImage image = new ScalingImage() { halign=Align.START, visible = true, max_width = MAX_WIDTH, max_height = MAX_HEIGHT }; ScalingImage image = new ScalingImage() { halign=Align.START, visible = true, max_width = MAX_WIDTH, max_height = MAX_HEIGHT };
Gdk.Pixbuf pixbuf; Gdk.Pixbuf pixbuf;
try { try {
pixbuf = new Gdk.Pixbuf.from_file(file_transfer.get_file().get_path()); pixbuf = new Gdk.Pixbuf.from_file(file.get_path());
} catch (Error error) { } catch (Error error) {
warning("Can't load picture %s - %s", file_transfer.get_file().get_path(), error.message); warning("Can't load picture %s - %s", file.get_path(), error.message);
Idle.add(get_image_widget.callback); Idle.add(get_image_widget.callback);
return null; return null;
} }
@ -112,8 +125,8 @@ public class FileWidget : Box {
Label url_label = builder.get_object("url_label") as Label; Label url_label = builder.get_object("url_label") as Label;
Util.force_color(url_label, "#eee"); Util.force_color(url_label, "#eee");
if (file_transfer.file_name != null && file_transfer.file_name != "") { if (file_name != null && file_name != "") {
string caption = file_transfer.file_name; string caption = file_name;
url_label.label = caption; url_label.label = caption;
} else { } else {
url_label.visible = false; url_label.visible = false;
@ -125,9 +138,9 @@ public class FileWidget : Box {
Util.force_css(open_button, "*:hover { background-color: rgba(255,255,255,0.3); border-color: transparent; }"); Util.force_css(open_button, "*:hover { background-color: rgba(255,255,255,0.3); border-color: transparent; }");
open_button.clicked.connect(() => { open_button.clicked.connect(() => {
try{ try{
AppInfo.launch_default_for_uri(file_transfer.get_file().get_uri(), null); AppInfo.launch_default_for_uri(file.get_uri(), null);
} catch (Error err) { } catch (Error err) {
info("Could not to open file://%s: %s", file_transfer.get_file().get_path(), err.message); info("Could not to open file://%s: %s", file.get_path(), err.message);
} }
}); });
@ -147,95 +160,47 @@ public class FileWidget : Box {
return event_box; return event_box;
} }
private static Gdk.Pixbuf crop_corners(Gdk.Pixbuf pixbuf, double radius = 3) { private bool show_image() {
Cairo.Context ctx = new Cairo.Context(new Cairo.ImageSurface(Cairo.Format.ARGB32, pixbuf.width, pixbuf.height)); if (file_transfer.mime_type == null || file_transfer.state != FileTransfer.State.COMPLETE) return false;
Gdk.cairo_set_source_pixbuf(ctx, pixbuf, 0, 0);
double degrees = Math.PI / 180.0; foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) {
ctx.new_sub_path(); foreach (string mime_type in pixbuf_format.get_mime_types()) {
ctx.arc(pixbuf.width - radius, radius, radius, -90 * degrees, 0 * degrees); if (mime_type == file_transfer.mime_type) {
ctx.arc(pixbuf.width - radius, pixbuf.height - radius, radius, 0 * degrees, 90 * degrees); return true;
ctx.arc(radius, pixbuf.height - radius, radius, 90 * degrees, 180 * degrees);
ctx.arc(radius, radius, radius, 180 * degrees, 270 * degrees);
ctx.close_path();
ctx.clip();
ctx.paint();
return Gdk.pixbuf_get_from_surface(ctx.get_target(), 0, 0, pixbuf.width, pixbuf.height);
} }
private Widget get_default_widget(FileTransfer file_transfer) {
string icon_name = get_file_icon_name(file_transfer.mime_type);
main_box = new Box(Orientation.HORIZONTAL, 10) { halign=Align.FILL, hexpand=true, visible=true };
content_type_image = new Image.from_icon_name(icon_name, IconSize.DND) { opacity=0.5, visible=true };
download_image = new Image.from_icon_name("dino-file-download-symbolic", IconSize.DND) { opacity=0.7, visible=true };
spinner = new Spinner() { visible=true };
EventBox stack_event_box = new EventBox() { visible=true };
image_stack = new Stack() { transition_type = StackTransitionType.CROSSFADE, transition_duration=50, valign=Align.CENTER, visible=true };
image_stack.add_named(download_image, "download_image");
image_stack.add_named(spinner, "spinner");
image_stack.add_named(content_type_image, "content_type_image");
stack_event_box.add(image_stack);
main_box.add(stack_event_box);
Box right_box = new Box(Orientation.VERTICAL, 0) { hexpand=true, visible=true };
Label name_label = new Label(file_transfer.file_name) { ellipsize=EllipsizeMode.MIDDLE, xalign=0, yalign=0, visible=true};
right_box.add(name_label);
EventBox mime_label_event_box = new EventBox() { visible=true };
mime_label = new Label("") { use_markup=true, xalign=0, yalign=1, visible=true};
mime_label_event_box.add(mime_label);
mime_label.get_style_context().add_class("dim-label");
right_box.add(mime_label_event_box);
main_box.add(right_box);
SizingBin bin = new SizingBin() { visible=true, hexpand=true, max_width=500, target_width=500 };
bin.add(main_box);
EventBox event_box = new EventBox() { margin_top=5, halign=Align.START, visible=true };
event_box.get_style_context().add_class("file-box-outer");
event_box.add(bin);
main_box.get_style_context().add_class("file-box");
event_box.enter_notify_event.connect((event) => {
pointer_inside = true;
Timeout.add(20, () => {
if (pointer_inside) {
event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.HAND2));
content_type_image.opacity = 0.7;
if (file_transfer.state == FileTransfer.State.NOT_STARTED) {
image_stack.set_visible_child_name("download_image");
} }
} }
return false; return false;
});
return false;
});
stack_event_box.enter_notify_event.connect((event) => { pointer_inside = true; return false; });
mime_label_event_box.enter_notify_event.connect((event) => { pointer_inside = true; return false; });
mime_label.enter_notify_event.connect((event) => { pointer_inside = true; return false; });
event_box.leave_notify_event.connect((event) => {
pointer_inside = false;
Timeout.add(20, () => {
if (!pointer_inside) {
event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.XTERM));
content_type_image.opacity = 0.5;
if (file_transfer.state == FileTransfer.State.NOT_STARTED) {
image_stack.set_visible_child_name("content_type_image");
} }
} }
return false;
}); public class FileDefaultWidgetController : Object {
return false;
}); private FileDefaultWidget widget;
stack_event_box.leave_notify_event.connect((event) => { pointer_inside = true; return false; }); private FileTransfer file_transfer;
mime_label_event_box.leave_notify_event.connect((event) => { pointer_inside = true; return false; }); private StreamInteractor stream_interactor;
mime_label.leave_notify_event.connect((event) => { pointer_inside = true; return false; });
event_box.button_release_event.connect((event_button) => { public FileDefaultWidgetController(FileDefaultWidget widget, FileTransfer file_transfer, StreamInteractor stream_interactor) {
this.widget = widget;
this.file_transfer = file_transfer;
this.stream_interactor = stream_interactor;
widget.name_label.label = file_transfer.file_name;
widget.button_release_event.connect(on_clicked);
file_transfer.notify["path"].connect(update_file_info);
file_transfer.notify["state"].connect(update_file_info);
file_transfer.notify["mime-type"].connect(update_file_info);
update_file_info();
}
private void update_file_info() {
widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
}
private bool on_clicked(EventButton event_button) {
switch (file_transfer.state) { switch (file_transfer.state) {
case FileTransfer.State.COMPLETE: case FileTransfer.State.COMPLETE:
if (event_button.button == 1) { if (event_button.button == 1) {
@ -251,105 +216,6 @@ public class FileWidget : Box {
break; break;
} }
return false; return false;
});
main_box.events = EventMask.POINTER_MOTION_MASK;
content_type_image.events = EventMask.POINTER_MOTION_MASK;
download_image.events = EventMask.POINTER_MOTION_MASK;
spinner.events = EventMask.POINTER_MOTION_MASK;
image_stack.events = EventMask.POINTER_MOTION_MASK;
right_box.events = EventMask.POINTER_MOTION_MASK;
name_label.events = EventMask.POINTER_MOTION_MASK;
mime_label.events = EventMask.POINTER_MOTION_MASK;
event_box.events = EventMask.POINTER_MOTION_MASK;
mime_label.events = EventMask.POINTER_MOTION_MASK;
mime_label_event_box.events = EventMask.POINTER_MOTION_MASK;
file_transfer.notify["path"].connect(update_file_info);
file_transfer.notify["state"].connect(update_file_info);
file_transfer.notify["mime-type"].connect(update_file_info);
update_file_info.begin();
return event_box;
}
private async void update_file_info() {
if (file_transfer.state == FileTransfer.State.COMPLETE && show_image() && state != State.IMAGE) {
this.remove(content);
this.add(yield get_image_widget(file_transfer));
state = State.IMAGE;
}
spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap
string? mime_description = file_transfer.mime_type != null ? ContentType.get_description(file_transfer.mime_type) : null;
switch (file_transfer.state) {
case FileTransfer.State.COMPLETE:
mime_label.label = "<span size='small'>" + mime_description + "</span>";
image_stack.set_visible_child_name("content_type_image");
break;
case FileTransfer.State.IN_PROGRESS:
mime_label.label = "<span size='small'>" + _("Downloading %s…").printf(get_size_string(file_transfer.size)) + "</span>";
spinner.active = true;
image_stack.set_visible_child_name("spinner");
break;
case FileTransfer.State.NOT_STARTED:
if (mime_description != null) {
mime_label.label = "<span size='small'>" + _("%s offered: %s").printf(mime_description, get_size_string(file_transfer.size)) + "</span>";
} else if (file_transfer.size != -1) {
mime_label.label = "<span size='small'>" + _("File offered: %s").printf(get_size_string(file_transfer.size)) + "</span>";
} else {
mime_label.label = "<span size='small'>" + _("File offered") + "</span>";
}
image_stack.set_visible_child_name("content_type_image");
break;
case FileTransfer.State.FAILED:
mime_label.label = "<span size='small' foreground=\"#f44336\">" + _("File transfer failed") + "</span>";
image_stack.set_visible_child_name("content_type_image");
break;
}
}
private static string get_file_icon_name(string? mime_type) {
if (mime_type == null) return "dino-file-symbolic";
string generic_icon_name = ContentType.get_generic_icon_name(mime_type) ?? "";
switch (generic_icon_name) {
case "audio-x-generic": return "dino-file-music-symbolic";
case "image-x-generic": return "dino-file-image-symbolic";
case "text-x-generic": return "dino-file-document-symbolic";
case "text-x-generic-template": return "dino-file-document-symbolic";
case "video-x-generic": return "dino-file-video-symbolic";
case "x-office-document": return "dino-file-document-symbolic";
case "x-office-spreadsheet": return "dino-file-table-symbolic";
default: return "dino-file-symbolic";
}
}
private static string get_size_string(int size) {
if (size < 1024) {
return @"$(size) B";
} else if (size < 1000 * 1000) {
return @"$(size / 1000) kB";
} else if (size < 1000 * 1000 * 1000) {
return @"$(size / 1000 / 1000) MB";
} else {
return @"$(size / 1000 / 1000 / 1000) GB";
}
}
private bool show_image() {
if (file_transfer.mime_type == null || file_transfer.state != FileTransfer.State.COMPLETE) return false;
foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) {
foreach (string mime_type in pixbuf_format.get_mime_types()) {
if (mime_type == file_transfer.mime_type) {
return true;
}
}
}
return false;
} }
} }