mirror of
https://gitlab.gnome.org/World/tootle
synced 2025-02-17 03:51:11 +01:00
Refactor attachment grid
* Introduce Slotted attachment grid * Use Stack in attachment Slot * Display attachment type * Fit pictures into the Slot center
This commit is contained in:
parent
3d0bd9e48e
commit
a542be90c1
17
data/app.css
17
data/app.css
@ -3,9 +3,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.attachment {
|
.attachment {
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
background: rgba (150, 150, 150, 0.2);
|
background: rgba (150, 150, 150, 0.2);
|
||||||
|
padding:0px;
|
||||||
|
margin:0px;
|
||||||
}
|
}
|
||||||
|
.attachment .pic {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.attachment .chip {
|
||||||
|
padding: 6px;
|
||||||
|
border-radius:6px;
|
||||||
|
}
|
||||||
|
/*.attachment box button:nth-child(1) {*/
|
||||||
|
/* border-radius:0 0 0 8px;*/
|
||||||
|
/*}*/
|
||||||
|
/*.attachment box button:nth-child(2) {*/
|
||||||
|
/* border-radius:0 8px 0 0;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
.header-title-button {
|
.header-title-button {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<file preprocess="xml-stripblanks">ui/widgets/accounts_button_item.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/accounts_button_item.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/profile_field_row.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/profile_field_row.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/timeline_filter.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/timeline_filter.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/widgets/attachment_slot.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/compose.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/compose.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/main.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/main.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/preferences.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/preferences.ui</file>
|
||||||
|
172
data/ui/widgets/attachment_slot.ui
Normal file
172
data/ui/widgets/attachment_slot.ui
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.36.0 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.22"/>
|
||||||
|
<template class="TootleWidgetsAttachmentSlot" parent="GtkFlowBoxChild">
|
||||||
|
<property name="height_request">180</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEventBox" id="event_box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="overlay">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="play_icon">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="margin_start">16</property>
|
||||||
|
<property name="margin_end">16</property>
|
||||||
|
<property name="margin_top">16</property>
|
||||||
|
<property name="margin_bottom">16</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="pixel_size">48</property>
|
||||||
|
<property name="icon_name">media-playback-start-symbolic</property>
|
||||||
|
<property name="icon_size">0</property>
|
||||||
|
<style>
|
||||||
|
<class name="chip"/>
|
||||||
|
<class name="osd"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="edit_bar">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_start">6</property>
|
||||||
|
<property name="margin_end">6</property>
|
||||||
|
<property name="margin_top">6</property>
|
||||||
|
<property name="margin_bottom">6</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="edit_btn">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Describe for the visually impaired</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">document-edit-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="remove_btn">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Remove</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">user-trash-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="linked"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="chip">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_start">6</property>
|
||||||
|
<property name="margin_end">6</property>
|
||||||
|
<property name="margin_top">6</property>
|
||||||
|
<property name="margin_bottom">6</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
<class name="chip"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="transition_type">crossfade</property>
|
||||||
|
<property name="interpolate_size">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinner" id="loading">
|
||||||
|
<property name="width_request">32</property>
|
||||||
|
<property name="height_request">32</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="opacity">0.5019607843137255</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">loading</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="attachment"/>
|
||||||
|
</style>
|
||||||
|
</template>
|
||||||
|
</interface>
|
@ -79,7 +79,8 @@ executable(
|
|||||||
'src/Widgets/Notification.vala',
|
'src/Widgets/Notification.vala',
|
||||||
'src/Widgets/VisibilityPopover.vala',
|
'src/Widgets/VisibilityPopover.vala',
|
||||||
'src/Widgets/Attachment/Box.vala',
|
'src/Widgets/Attachment/Box.vala',
|
||||||
'src/Widgets/Attachment/Item.vala',
|
'src/Widgets/Attachment/Slot.vala',
|
||||||
|
'src/Widgets/Attachment/Picture.vala',
|
||||||
'src/Dialogs/ISavedWindow.vala',
|
'src/Dialogs/ISavedWindow.vala',
|
||||||
'src/Dialogs/MainWindow.vala',
|
'src/Dialogs/MainWindow.vala',
|
||||||
'src/Dialogs/Compose.vala',
|
'src/Dialogs/Compose.vala',
|
||||||
|
@ -5,7 +5,7 @@ public class Tootle.API.Attachment : Entity {
|
|||||||
public string url { get; set; }
|
public string url { get; set; }
|
||||||
public string? description { get; set; }
|
public string? description { get; set; }
|
||||||
public string? _preview_url { get; set; }
|
public string? _preview_url { get; set; }
|
||||||
public string preview_url {
|
public string? preview_url {
|
||||||
set { this._preview_url = value; }
|
set { this._preview_url = value; }
|
||||||
get { return (this._preview_url == null || this._preview_url == "") ? url : _preview_url; }
|
get { return (this._preview_url == null || this._preview_url == "") ? url : _preview_url; }
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,11 @@ public class Tootle.Desktop {
|
|||||||
return theme.has_icon (fallback) ? fallback : fallback2;
|
return theme.has_icon (fallback) ? fallback : fallback2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Gdk.Pixbuf icon_to_pixbuf (string name) {
|
||||||
|
var theme = Gtk.IconTheme.get_default ();
|
||||||
|
return theme.load_icon (name, 32, Gtk.IconLookupFlags.GENERIC_FALLBACK);
|
||||||
|
}
|
||||||
|
|
||||||
public static void set_hotkey_tooltip (Gtk.Widget widget, string? description, string[] accelerators) {
|
public static void set_hotkey_tooltip (Gtk.Widget widget, string? description, string[] accelerators) {
|
||||||
widget.tooltip_markup = Granite.markup_accel_tooltip (accelerators, description);
|
widget.tooltip_markup = Granite.markup_accel_tooltip (accelerators, description);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ public class Tootle.Dialogs.Compose : Window {
|
|||||||
public string label { get; construct set; }
|
public string label { get; construct set; }
|
||||||
public int char_limit {
|
public int char_limit {
|
||||||
get {
|
get {
|
||||||
return 250;
|
return 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +62,7 @@ public class Tootle.Dialogs.Compose : Window {
|
|||||||
}
|
}
|
||||||
content.buffer.text = Html.remove_tags (status.content);
|
content.buffer.text = Html.remove_tags (status.content);
|
||||||
|
|
||||||
|
validate ();
|
||||||
show ();
|
show ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,12 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
|
|||||||
timeline_switcher.valign = Align.FILL;
|
timeline_switcher.valign = Align.FILL;
|
||||||
timeline_stack.notify["visible-child"].connect (on_timeline_changed);
|
timeline_stack.notify["visible-child"].connect (on_timeline_changed);
|
||||||
|
|
||||||
add_timeline_view (new Views.Home (), app.ACCEL_TIMELINE_0, 0);
|
add_timeline_view (new Views.Bookmarks (), app.ACCEL_TIMELINE_0, 0);
|
||||||
add_timeline_view (new Views.Notifications (), app.ACCEL_TIMELINE_1, 1);
|
add_timeline_view (new Views.Home (), app.ACCEL_TIMELINE_1, 1);
|
||||||
add_timeline_view (new Views.Local (), app.ACCEL_TIMELINE_2, 2);
|
// add_timeline_view (new Views.Home (), app.ACCEL_TIMELINE_0, 0);
|
||||||
add_timeline_view (new Views.Federated (), app.ACCEL_TIMELINE_3, 3);
|
// add_timeline_view (new Views.Notifications (), app.ACCEL_TIMELINE_1, 1);
|
||||||
|
// add_timeline_view (new Views.Local (), app.ACCEL_TIMELINE_2, 2);
|
||||||
|
// add_timeline_view (new Views.Federated (), app.ACCEL_TIMELINE_3, 3);
|
||||||
|
|
||||||
settings.bind_property ("dark-theme", Gtk.Settings.get_default (), "gtk-application-prefer-dark-theme", BindingFlags.SYNC_CREATE);
|
settings.bind_property ("dark-theme", Gtk.Settings.get_default (), "gtk-application-prefer-dark-theme", BindingFlags.SYNC_CREATE);
|
||||||
settings.notify["post-text-size"].connect (() => on_zoom_level_changed ());
|
settings.notify["post-text-size"].connect (() => on_zoom_level_changed ());
|
||||||
|
@ -13,24 +13,4 @@ public class Tootle.Drawing {
|
|||||||
ctx.close_path ();
|
ctx.close_path ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void center (Cairo.Context ctx, int w, int h, int tw, int th) {
|
|
||||||
var cx = w/2 - tw/2;
|
|
||||||
var cy = h/2 - th/2;
|
|
||||||
ctx.translate (cx, cy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Pixbuf make_thumbnail (Pixbuf pb, int view_w, int view_h) {
|
|
||||||
if (view_w >= pb.width && view_h >= pb.height)
|
|
||||||
return pb;
|
|
||||||
|
|
||||||
double ratio_x = (double) view_w / (double) pb.width;
|
|
||||||
double ratio_y = (double) view_h / (double) pb.height;
|
|
||||||
double ratio = ratio_x < ratio_y ? ratio_x : ratio_y;
|
|
||||||
|
|
||||||
return pb.scale_simple (
|
|
||||||
(int) (pb.width * ratio),
|
|
||||||
(int) (pb.height * ratio),
|
|
||||||
InterpType.BILINEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -81,10 +81,22 @@ public class Tootle.Cache : GLib.Object {
|
|||||||
id = msg.finished.connect (() => {
|
id = msg.finished.connect (() => {
|
||||||
Pixbuf? pixbuf = null;
|
Pixbuf? pixbuf = null;
|
||||||
|
|
||||||
var data = msg.response_body.flatten ().data;
|
try {
|
||||||
var stream = new MemoryInputStream.from_data (data);
|
var code = message.status_code;
|
||||||
pixbuf = new Pixbuf.from_stream (stream);
|
if (code != Soup.Status.OK) {
|
||||||
stream.close ();
|
var msg = network.describe_error (code);
|
||||||
|
throw new Oopsie.INSTANCE (@"Server returned $msg");
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = message.response_body.flatten ().data;
|
||||||
|
var stream = new MemoryInputStream.from_data (data);
|
||||||
|
pixbuf = new Pixbuf.from_stream (stream);
|
||||||
|
stream.close ();
|
||||||
|
}
|
||||||
|
catch (Error e) {
|
||||||
|
warning (@"\"$url\" -> Pixbuf: FAIL ($(e.message))");
|
||||||
|
pixbuf = Desktop.icon_to_pixbuf ("image-x-generic-symbolic");
|
||||||
|
}
|
||||||
|
|
||||||
// message (@"[*] $key");
|
// message (@"[*] $key");
|
||||||
items[key] = new Item (pixbuf, 1);
|
items[key] = new Item (pixbuf, 1);
|
||||||
|
@ -68,7 +68,7 @@ public class Tootle.Network : GLib.Object {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public string describe_error (int32 code) {
|
public string describe_error (uint code) {
|
||||||
var reason = Soup.Status.get_phrase (code);
|
var reason = Soup.Status.get_phrase (code);
|
||||||
return @"$code: $reason";
|
return @"$code: $reason";
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ public class Tootle.Widgets.Attachment.Box : FlowBox {
|
|||||||
construct {
|
construct {
|
||||||
hexpand = true;
|
hexpand = true;
|
||||||
can_focus = false;
|
can_focus = false;
|
||||||
|
column_spacing = row_spacing = 8;
|
||||||
selection_mode = SelectionMode.NONE;
|
selection_mode = SelectionMode.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ public class Tootle.Widgets.Attachment.Box : FlowBox {
|
|||||||
Object (editing: editing);
|
Object (editing: editing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Upload attachments in Compose dialog
|
||||||
public void select () {
|
public void select () {
|
||||||
var filter = new Gtk.FileFilter ();
|
var filter = new Gtk.FileFilter ();
|
||||||
filter.add_mime_type ("image/jpeg");
|
filter.add_mime_type ("image/jpeg");
|
||||||
@ -49,20 +51,27 @@ public class Tootle.Widgets.Attachment.Box : FlowBox {
|
|||||||
public bool populate (ArrayList<API.Attachment>? list) {
|
public bool populate (ArrayList<API.Attachment>? list) {
|
||||||
if (list == null)
|
if (list == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var max = 6;
|
var max = 2;
|
||||||
if (list.size % 2 == 0)
|
var min = 1;
|
||||||
max = 2;
|
if (list.size == 1)
|
||||||
|
max = 1;
|
||||||
//max_children_per_line = (int)Math.fmin (list.size, 5);
|
else if (list.size % 2 == 0)
|
||||||
|
max = min = 2;
|
||||||
|
else if (list.size % 3 == 0)
|
||||||
|
max = min = 3;
|
||||||
|
|
||||||
max_children_per_line = max;
|
max_children_per_line = max;
|
||||||
|
min_children_per_line = min;
|
||||||
list.@foreach (obj => pack (obj));
|
list.@foreach (obj => pack (obj));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool pack (API.Attachment obj) {
|
public bool pack (API.Attachment obj) {
|
||||||
var w = new Widgets.Attachment.Item (obj);
|
var w = new Widgets.Attachment.Slot (obj);
|
||||||
insert (w, -1);
|
insert (w, -1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
using Gtk;
|
|
||||||
using Gdk;
|
|
||||||
|
|
||||||
public class Tootle.Widgets.Attachment.Item : EventBox {
|
|
||||||
|
|
||||||
public API.Attachment attachment { get; construct set; }
|
|
||||||
|
|
||||||
private Cache.Reference? cached;
|
|
||||||
|
|
||||||
public Item (API.Attachment obj) {
|
|
||||||
Object (attachment: obj);
|
|
||||||
}
|
|
||||||
~Item () {
|
|
||||||
cache.unload (cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
construct {
|
|
||||||
get_style_context ().add_class ("attachment");
|
|
||||||
width_request = height_request = 128;
|
|
||||||
hexpand = true;
|
|
||||||
tooltip_text = attachment.description ?? _("No description is available");
|
|
||||||
|
|
||||||
button_press_event.connect (on_clicked);
|
|
||||||
|
|
||||||
show ();
|
|
||||||
on_request ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void on_request () {
|
|
||||||
cached = null;
|
|
||||||
on_redraw ();
|
|
||||||
cache.load (attachment.preview_url, on_cache_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void on_redraw () {
|
|
||||||
var w = get_allocated_width ();
|
|
||||||
var h = get_allocated_height ();
|
|
||||||
queue_draw_area (0, 0, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void on_cache_result (Cache.Reference? result) {
|
|
||||||
cached = result;
|
|
||||||
on_redraw ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void download () {
|
|
||||||
Desktop.download (attachment.url, path => {
|
|
||||||
app.toast (_("Attachment downloaded"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
protected void open () {
|
|
||||||
Desktop.download (attachment.url, path => {
|
|
||||||
Desktop.open_uri (path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool draw (Cairo.Context ctx) {
|
|
||||||
base.draw (ctx);
|
|
||||||
var w = get_allocated_width ();
|
|
||||||
var h = get_allocated_height ();
|
|
||||||
var style = get_style_context ();
|
|
||||||
var border_radius = style.get_property (Gtk.STYLE_PROPERTY_BORDER_RADIUS, style.get_state ()).get_int ();
|
|
||||||
|
|
||||||
if (cached != null) {
|
|
||||||
if (cached.loading) {
|
|
||||||
Drawing.center (ctx, w, h, 32, 32);
|
|
||||||
get_style_context ().render_activity (ctx, 0, 0, 32, 32);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var thumb = Drawing.make_thumbnail (cached.data, w, h);
|
|
||||||
Drawing.draw_rounded_rect (ctx, 0, 0, w, h, border_radius);
|
|
||||||
Drawing.center (ctx, w, h, thumb.width, thumb.height);
|
|
||||||
Gdk.cairo_set_source_pixbuf (ctx, thumb, 0, 0);
|
|
||||||
ctx.fill ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Gdk.EVENT_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool on_clicked (EventButton ev) {
|
|
||||||
if (ev.button == 1) {
|
|
||||||
open ();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (ev.button == 3) {
|
|
||||||
var menu = new Gtk.Menu ();
|
|
||||||
|
|
||||||
var item_open = new Gtk.MenuItem.with_label (_("Open"));
|
|
||||||
item_open.activate.connect (open);
|
|
||||||
menu.add (item_open);
|
|
||||||
|
|
||||||
var item_download = new Gtk.MenuItem.with_label (_("Download"));
|
|
||||||
item_download.activate.connect (download);
|
|
||||||
menu.add (item_download);
|
|
||||||
|
|
||||||
menu.show_all ();
|
|
||||||
menu.attach_widget = this;
|
|
||||||
menu.popup_at_pointer ();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
91
src/Widgets/Attachment/Picture.vala
Normal file
91
src/Widgets/Attachment/Picture.vala
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using Gtk;
|
||||||
|
using Gdk;
|
||||||
|
|
||||||
|
public class Tootle.Widgets.Attachment.Picture : DrawingArea {
|
||||||
|
|
||||||
|
public string url { get; set; }
|
||||||
|
|
||||||
|
Cache.Reference? cached;
|
||||||
|
|
||||||
|
construct {
|
||||||
|
hexpand = vexpand = true;
|
||||||
|
get_style_context ().add_class ("pic");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Picture (string url) {
|
||||||
|
Object (url: url);
|
||||||
|
}
|
||||||
|
~Picture () {
|
||||||
|
cache.unload (cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void on_request () {
|
||||||
|
cached = null;
|
||||||
|
on_redraw ();
|
||||||
|
cache.load (url, on_cache_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_cache_update (Cache.Reference? result) {
|
||||||
|
cached = result;
|
||||||
|
if (cached != null)
|
||||||
|
visible = !cached.loading;
|
||||||
|
on_redraw ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_redraw () {
|
||||||
|
var w = get_allocated_width ();
|
||||||
|
var h = get_allocated_height ();
|
||||||
|
queue_draw_area (0, 0, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_ratio (int w, int h) {
|
||||||
|
var ow = cached.data.get_width ();
|
||||||
|
var oh = cached.data.get_height ();
|
||||||
|
var xscale = (float) w / ow;
|
||||||
|
var yscale = (float) h / oh;
|
||||||
|
|
||||||
|
if (xscale > yscale)
|
||||||
|
return xscale;
|
||||||
|
else
|
||||||
|
return yscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool draw (Cairo.Context ctx) {
|
||||||
|
var w = get_allocated_width ();
|
||||||
|
var h = get_allocated_height ();
|
||||||
|
var style = get_style_context ();
|
||||||
|
var border_radius = style.get_property (Gtk.STYLE_PROPERTY_BORDER_RADIUS, style.get_state ()).get_int ();
|
||||||
|
|
||||||
|
if (cached != null) {
|
||||||
|
if (!cached.loading) {
|
||||||
|
Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf (cached.data, 1, null);
|
||||||
|
|
||||||
|
ctx.save ();
|
||||||
|
Drawing.draw_rounded_rect (ctx, 0, 0, w, h, border_radius);
|
||||||
|
|
||||||
|
//Proportionally scale to fit into the allocated container
|
||||||
|
var ratio = get_ratio (w, h);
|
||||||
|
ctx.scale (ratio, ratio);
|
||||||
|
|
||||||
|
//Center the result
|
||||||
|
var oh = cached.data.get_height ();
|
||||||
|
var result_h = oh*ratio;
|
||||||
|
var offset_y = (h - result_h) / 2;
|
||||||
|
|
||||||
|
var ow = cached.data.get_width ();
|
||||||
|
var result_w = ow*ratio;
|
||||||
|
var offset_x = (w - result_w) / 2;
|
||||||
|
|
||||||
|
ctx.translate (offset_x, offset_y);
|
||||||
|
|
||||||
|
//Draw it
|
||||||
|
ctx.set_source_surface (surface, 0, 0);
|
||||||
|
ctx.fill ();
|
||||||
|
ctx.restore ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
src/Widgets/Attachment/Slot.vala
Normal file
68
src/Widgets/Attachment/Slot.vala
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using Gtk;
|
||||||
|
using Gdk;
|
||||||
|
|
||||||
|
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/attachment_slot.ui")]
|
||||||
|
public class Tootle.Widgets.Attachment.Slot : FlowBoxChild {
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
EventBox event_box;
|
||||||
|
[GtkChild]
|
||||||
|
Label chip;
|
||||||
|
[GtkChild]
|
||||||
|
Image play_icon;
|
||||||
|
[GtkChild]
|
||||||
|
Stack stack;
|
||||||
|
|
||||||
|
public API.Attachment attachment { get; construct set; }
|
||||||
|
|
||||||
|
public Slot (API.Attachment obj) {
|
||||||
|
Object (attachment: obj);
|
||||||
|
|
||||||
|
if (attachment.preview_url != null) {
|
||||||
|
var img = new Widgets.Attachment.Picture (attachment.preview_url);
|
||||||
|
img.notify["visible"].connect (() => {
|
||||||
|
stack.visible_child_name = img.visible ? "content" : "loading";
|
||||||
|
});
|
||||||
|
stack.add_named (img, "content");
|
||||||
|
img.on_request ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachment.kind != "image") {
|
||||||
|
chip.label = attachment.kind;
|
||||||
|
chip.show ();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (attachment.kind) {
|
||||||
|
case "audio":
|
||||||
|
case "video":
|
||||||
|
case "gifv":
|
||||||
|
play_icon.show ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct {
|
||||||
|
event_box.tooltip_text = attachment.description;
|
||||||
|
event_box.button_release_event.connect (on_clicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void download () {
|
||||||
|
Desktop.download (attachment.url, path => {
|
||||||
|
app.toast (_("Attachment downloaded"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void open () {
|
||||||
|
Desktop.download (attachment.url, path => {
|
||||||
|
Desktop.open_uri (path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool on_clicked (EventButton ev) {
|
||||||
|
if (ev.button != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
open ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,7 +42,6 @@ public class Tootle.Widgets.Avatar : EventBox {
|
|||||||
|
|
||||||
public int get_scaled_size () {
|
public int get_scaled_size () {
|
||||||
return size; //return size * get_scale_factor ();
|
return size; //return size * get_scale_factor ();
|
||||||
}
|
|
||||||
|
|
||||||
private void on_redraw () {
|
private void on_redraw () {
|
||||||
set_size_request (get_scaled_size (), get_scaled_size ());
|
set_size_request (get_scaled_size (), get_scaled_size ());
|
||||||
@ -62,7 +61,7 @@ public class Tootle.Widgets.Avatar : EventBox {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pixbuf = IconTheme.get_default ()
|
pixbuf = IconTheme.get_default ()
|
||||||
.load_icon_for_scale ("avatar-default", size, get_scale_factor (), IconLookupFlags.GENERIC_FALLBACK);
|
.load_icon_for_scale ("avatar-default", get_scaled_size (), get_scale_factor (), IconLookupFlags.GENERIC_FALLBACK);
|
||||||
}
|
}
|
||||||
Gdk.cairo_set_source_pixbuf (ctx, pixbuf, 0, 0);
|
Gdk.cairo_set_source_pixbuf (ctx, pixbuf, 0, 0);
|
||||||
ctx.fill ();
|
ctx.fill ();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user