Add hover effect on conversation content
This commit is contained in:
parent
dd6c860aed
commit
8aab18c1ec
|
@ -19,14 +19,13 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="margin">15</property>
|
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="main">
|
<object class="GtkBox" id="main">
|
||||||
|
<property name="margin-bottom">15</property>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="spacing">15</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -37,6 +37,10 @@ window.dino-main .dino-conversation .highlight-once {
|
||||||
animation-name: highlight;
|
animation-name: highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.dino-main .dino-conversation .message-box:hover {
|
||||||
|
background: alpha(@theme_fg_color, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
window.dino-main .dino-sidebar > frame {
|
window.dino-main .dino-sidebar > frame {
|
||||||
background: @insensitive_bg_color;
|
background: @insensitive_bg_color;
|
||||||
border-left: 1px solid @borders;
|
border-left: 1px solid @borders;
|
||||||
|
|
|
@ -5,8 +5,8 @@ using Xmpp;
|
||||||
namespace Dino.Ui {
|
namespace Dino.Ui {
|
||||||
|
|
||||||
public class AvatarImage : Misc {
|
public class AvatarImage : Misc {
|
||||||
public int height { get; set; default = 32; }
|
public int height { get; set; default = 35; }
|
||||||
public int width { get; set; default = 32; }
|
public int width { get; set; default = 35; }
|
||||||
public bool allow_gray { get; set; default = true; }
|
public bool allow_gray { get; set; default = true; }
|
||||||
public Account account { get; private set; }
|
public Account account { get; private set; }
|
||||||
public StreamInteractor stream_interactor { get; set; }
|
public StreamInteractor stream_interactor { get; set; }
|
||||||
|
|
|
@ -33,8 +33,6 @@ public class ConversationSelector : ListBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
this.stream_interactor = stream_interactor;
|
|
||||||
|
|
||||||
get_style_context().add_class("sidebar");
|
get_style_context().add_class("sidebar");
|
||||||
set_filter_func(filter);
|
set_filter_func(filter);
|
||||||
set_header_func(header);
|
set_header_func(header);
|
||||||
|
|
|
@ -178,6 +178,7 @@ public class FileItemWidgetGenerator : WidgetGenerator, Object {
|
||||||
grid.attach(image, 0, 0, 1, 1);
|
grid.attach(image, 0, 0, 1, 1);
|
||||||
|
|
||||||
EventBox event_box = new EventBox() { halign=Align.START, visible=true };
|
EventBox event_box = new EventBox() { halign=Align.START, visible=true };
|
||||||
|
event_box.events = EventMask.POINTER_MOTION_MASK;
|
||||||
event_box.add(grid);
|
event_box.add(grid);
|
||||||
event_box.enter_notify_event.connect(() => { toolbar_revealer.reveal_child = true; return false; });
|
event_box.enter_notify_event.connect(() => { toolbar_revealer.reveal_child = true; return false; });
|
||||||
event_box.leave_notify_event.connect(() => { toolbar_revealer.reveal_child = false; return false; });
|
event_box.leave_notify_event.connect(() => { toolbar_revealer.reveal_child = false; return false; });
|
||||||
|
|
|
@ -7,22 +7,25 @@ using Dino.Entities;
|
||||||
|
|
||||||
namespace Dino.Ui.ConversationSummary {
|
namespace Dino.Ui.ConversationSummary {
|
||||||
|
|
||||||
public class ConversationItemSkeleton : Box {
|
public class ConversationItemSkeleton : EventBox {
|
||||||
|
|
||||||
private AvatarImage image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true, allow_gray = false };
|
private AvatarImage image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true, allow_gray = false };
|
||||||
|
|
||||||
|
public bool show_skeleton { get; set; }
|
||||||
|
public bool last_group_item { get; set; }
|
||||||
|
|
||||||
public StreamInteractor stream_interactor;
|
public StreamInteractor stream_interactor;
|
||||||
public Conversation conversation { get; set; }
|
public Conversation conversation { get; set; }
|
||||||
public ArrayList<Plugins.MetaConversationItem> items = new ArrayList<Plugins.MetaConversationItem>();
|
public Plugins.MetaConversationItem item;
|
||||||
|
|
||||||
private Grid grid = new Grid() { visible=true };
|
private Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
|
||||||
private HashMap<Plugins.MetaConversationItem, Widget> item_widgets = new HashMap<Plugins.MetaConversationItem, Widget>();
|
private Box header_content_box = new Box(Orientation.VERTICAL, 0) { visible=true };
|
||||||
private DefaultSkeletonHeader default_header;
|
private DefaultSkeletonHeader default_header;
|
||||||
|
|
||||||
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
|
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
this.stream_interactor = stream_interactor;
|
this.stream_interactor = stream_interactor;
|
||||||
Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
|
this.get_style_context().add_class("message-box");
|
||||||
|
|
||||||
if (item.requires_avatar) {
|
if (item.requires_avatar) {
|
||||||
image.set_jid(stream_interactor, item.jid, conversation.account);
|
image.set_jid(stream_interactor, item.jid, conversation.account);
|
||||||
|
@ -34,30 +37,44 @@ public class ConversationItemSkeleton : Box {
|
||||||
default_header.name_label.visible = false;
|
default_header.name_label.visible = false;
|
||||||
default_header.dot_label.visible = false;
|
default_header.dot_label.visible = false;
|
||||||
}
|
}
|
||||||
grid.attach(default_header, 0, 0, 1, 1);
|
header_content_box.add(default_header);
|
||||||
}
|
}
|
||||||
add_meta_item(item);
|
|
||||||
|
|
||||||
image_content_box.add(grid);
|
// add item
|
||||||
this.add(image_content_box);
|
this.item = item;
|
||||||
}
|
|
||||||
|
|
||||||
public void add_meta_item(Plugins.MetaConversationItem item) {
|
|
||||||
items.add(item);
|
|
||||||
if (default_header != null) {
|
if (default_header != null) {
|
||||||
default_header.add_item(item);
|
default_header.add_item(item);
|
||||||
}
|
}
|
||||||
Widget? widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
|
Widget? widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
|
||||||
|
widget.valign = Align.END;
|
||||||
if (widget != null) {
|
if (widget != null) {
|
||||||
grid.attach(widget, 0, items.size, 1, 1);
|
header_content_box.add(widget);
|
||||||
item_widgets[item] = widget;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void remove_meta_item(Plugins.MetaConversationItem item) {
|
image_content_box.add(header_content_box);
|
||||||
item_widgets[item].destroy();
|
this.add(image_content_box);
|
||||||
item_widgets.unset(item);
|
|
||||||
items.remove(item);
|
if (item.get_type().is_a(typeof(ContentMetaItem))) {
|
||||||
|
this.motion_notify_event.connect((event) => {
|
||||||
|
this.set_state_flags(StateFlags.PRELIGHT, false);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
this.enter_notify_event.connect((event) => {
|
||||||
|
this.set_state_flags(StateFlags.PRELIGHT, false);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
this.leave_notify_event.connect((event) => {
|
||||||
|
this.unset_state_flags(StateFlags.PRELIGHT);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notify["show-skeleton"].connect(update_margin);
|
||||||
|
this.notify["last-group-item"].connect(update_margin);
|
||||||
|
|
||||||
|
this.show_skeleton = true;
|
||||||
|
this.last_group_item = true;
|
||||||
|
update_margin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update_time() {
|
public void update_time() {
|
||||||
|
@ -65,13 +82,29 @@ public class ConversationItemSkeleton : Box {
|
||||||
default_header.update_time();
|
default_header.update_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update_margin() {
|
||||||
|
image.visible = this.show_skeleton;
|
||||||
|
if (default_header != null) {
|
||||||
|
default_header.visible = this.show_skeleton;
|
||||||
|
}
|
||||||
|
image_content_box.margin_start = this.show_skeleton ? 15 : 58;
|
||||||
|
|
||||||
|
if (this.show_skeleton && this.last_group_item) {
|
||||||
|
image_content_box.margin_top = 8;
|
||||||
|
image_content_box.margin_bottom = 8;
|
||||||
|
} else {
|
||||||
|
image_content_box.margin_top = 4;
|
||||||
|
image_content_box.margin_bottom = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultSkeletonHeader : Box {
|
public class DefaultSkeletonHeader : Box {
|
||||||
private Box box = new Box(Orientation.HORIZONTAL, 4) { visible=true };
|
private Box box = new Box(Orientation.HORIZONTAL, 4) { visible=true };
|
||||||
public Label name_label = new Label("") { use_markup=true, xalign=0, visible=true };
|
public Label name_label = new Label("") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
||||||
public Label time_label = new Label("") { use_markup=true, xalign=0, visible=true };
|
public Label time_label = new Label("") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
||||||
public Label dot_label = new Label("<span size='small'>·</span>") { use_markup=true, xalign=0, visible=true };
|
public Label dot_label = new Label("<span size='small'>·</span>") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
||||||
public Image encryption_image = new Image();
|
public Image encryption_image = new Image();
|
||||||
public Image received_image = new Image();
|
public Image received_image = new Image();
|
||||||
|
|
||||||
|
|
|
@ -98,26 +98,19 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
if (after_items.size == 40) {
|
if (after_items.size == 40) {
|
||||||
at_current_content = false;
|
at_current_content = false;
|
||||||
}
|
}
|
||||||
{
|
|
||||||
int h = 0, i = 0;
|
|
||||||
main.@foreach((widget) => {
|
|
||||||
if (i >= before_items.size) return;
|
|
||||||
ConversationItemSkeleton? sk = widget as ConversationItemSkeleton;
|
|
||||||
i += sk != null ? sk.items.size : 1;
|
|
||||||
int minimum_height, natural_height;
|
|
||||||
widget.get_preferred_height_for_width(main.get_allocated_width() - 2 * main.margin, out minimum_height, out natural_height);
|
|
||||||
h += minimum_height + 15;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Compute where to jump to for centered message, jump, highlight.
|
||||||
reload_messages = false;
|
reload_messages = false;
|
||||||
Timeout.add(700, () => {
|
Timeout.add(700, () => {
|
||||||
int h = 0, i = 0;
|
int h = 0, i = 0;
|
||||||
|
bool @break = false;
|
||||||
main.@foreach((widget) => {
|
main.@foreach((widget) => {
|
||||||
if (i >= before_items.size) return;
|
if (widget == w || @break) {
|
||||||
ConversationItemSkeleton? sk = widget as ConversationItemSkeleton;
|
@break = true;
|
||||||
i += sk != null ? sk.items.size : 1;
|
return;
|
||||||
h += widget.get_allocated_height() + 15;
|
}
|
||||||
|
h += widget.get_allocated_height();
|
||||||
|
i++;
|
||||||
});
|
});
|
||||||
scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
|
scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
|
||||||
w.get_style_context().add_class("highlight-once");
|
w.get_style_context().add_class("highlight-once");
|
||||||
|
@ -183,14 +176,12 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
|
|
||||||
public void do_insert_item(Plugins.MetaConversationItem item) {
|
public void do_insert_item(Plugins.MetaConversationItem item) {
|
||||||
lock (meta_items) {
|
lock (meta_items) {
|
||||||
if (!item.can_merge || !merge_back(item)) {
|
insert_new(item);
|
||||||
insert_new(item);
|
if (item as ContentMetaItem != null) {
|
||||||
|
content_items.add(item);
|
||||||
}
|
}
|
||||||
|
meta_items.add(item);
|
||||||
}
|
}
|
||||||
if (item as ContentMetaItem != null) {
|
|
||||||
content_items.add(item);
|
|
||||||
}
|
|
||||||
meta_items.add(item);
|
|
||||||
|
|
||||||
inserted_item(item);
|
inserted_item(item);
|
||||||
}
|
}
|
||||||
|
@ -198,15 +189,12 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
private void remove_item(Plugins.MetaConversationItem item) {
|
private void remove_item(Plugins.MetaConversationItem item) {
|
||||||
ConversationItemSkeleton? skeleton = item_item_skeletons[item];
|
ConversationItemSkeleton? skeleton = item_item_skeletons[item];
|
||||||
if (skeleton != null) {
|
if (skeleton != null) {
|
||||||
if (skeleton.items.size > 1) {
|
widgets[item].destroy();
|
||||||
skeleton.remove_meta_item(item);
|
widgets.unset(item);
|
||||||
} else {
|
skeleton.destroy();
|
||||||
widgets[item].destroy();
|
item_skeletons.remove(skeleton);
|
||||||
widgets.unset(item);
|
item_item_skeletons.unset(item);
|
||||||
skeleton.destroy();
|
|
||||||
item_skeletons.remove(skeleton);
|
|
||||||
item_item_skeletons.unset(item);
|
|
||||||
}
|
|
||||||
content_items.remove(item);
|
content_items.remove(item);
|
||||||
meta_items.remove(item);
|
meta_items.remove(item);
|
||||||
}
|
}
|
||||||
|
@ -242,41 +230,9 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
widget.destroy();
|
widget.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool merge_back(Plugins.MetaConversationItem item) {
|
|
||||||
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
|
|
||||||
if (lower_item != null) {
|
|
||||||
ConversationItemSkeleton lower_skeleton = item_item_skeletons[lower_item];
|
|
||||||
Plugins.MetaConversationItem lower_start_item = lower_skeleton.items[0];
|
|
||||||
if (lower_start_item.can_merge &&
|
|
||||||
item.display_time.difference(lower_start_item.display_time) < TimeSpan.MINUTE &&
|
|
||||||
lower_start_item.jid.equals(item.jid) &&
|
|
||||||
lower_start_item.encryption == item.encryption &&
|
|
||||||
(item.mark == Message.Marked.WONTSEND) == (lower_start_item.mark == Message.Marked.WONTSEND)) {
|
|
||||||
lower_skeleton.add_meta_item(item);
|
|
||||||
|
|
||||||
widgets[item] = widgets[lower_start_item];
|
|
||||||
item_item_skeletons[item] = lower_skeleton;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Widget insert_new(Plugins.MetaConversationItem item) {
|
private Widget insert_new(Plugins.MetaConversationItem item) {
|
||||||
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
|
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
|
||||||
|
|
||||||
// Does another skeleton need to be split?
|
|
||||||
if (lower_item != null) {
|
|
||||||
ConversationItemSkeleton lower_skeleton = item_item_skeletons[lower_item];
|
|
||||||
if (lower_skeleton.items.size > 1) {
|
|
||||||
Plugins.MetaConversationItem lower_end_item = lower_skeleton.items[lower_skeleton.items.size - 1];
|
|
||||||
if (item.sort_time.compare(lower_end_item.sort_time) < 0) {
|
|
||||||
split_at_time(lower_skeleton, item.sort_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill datastructure
|
// Fill datastructure
|
||||||
ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item) { visible=true };
|
ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item) { visible=true };
|
||||||
item_item_skeletons[item] = item_skeleton;
|
item_item_skeletons[item] = item_skeleton;
|
||||||
|
@ -297,6 +253,19 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
widgets[item] = insert;
|
widgets[item] = insert;
|
||||||
main.reorder_child(insert, index);
|
main.reorder_child(insert, index);
|
||||||
|
|
||||||
|
if (lower_item != null) {
|
||||||
|
ConversationItemSkeleton lower_skeleton = item_item_skeletons[lower_item];
|
||||||
|
Plugins.MetaConversationItem lower_start_item = lower_skeleton.item;
|
||||||
|
if (item.display_time != null && lower_start_item.display_time != null &&
|
||||||
|
item.display_time.difference(lower_start_item.display_time) < TimeSpan.MINUTE &&
|
||||||
|
lower_start_item.jid.equals(item.jid) &&
|
||||||
|
lower_start_item.encryption == item.encryption &&
|
||||||
|
(item.mark == Message.Marked.WONTSEND) == (lower_start_item.mark == Message.Marked.WONTSEND)) {
|
||||||
|
item_skeleton.show_skeleton = false;
|
||||||
|
lower_skeleton.last_group_item = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If an item from the past was added, add everything between that item and the (post-)first present item
|
// If an item from the past was added, add everything between that item and the (post-)first present item
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
Dino.Application app = Dino.Application.get_default();
|
Dino.Application app = Dino.Application.get_default();
|
||||||
|
@ -313,24 +282,6 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
||||||
return insert;
|
return insert;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void split_at_time(ConversationItemSkeleton split_skeleton, DateTime time) {
|
|
||||||
bool already_divided = false;
|
|
||||||
int i = 0;
|
|
||||||
while(i < split_skeleton.items.size) {
|
|
||||||
Plugins.MetaConversationItem meta_item = split_skeleton.items[i];
|
|
||||||
if (time.compare(meta_item.display_time) < 0) {
|
|
||||||
remove_item(meta_item);
|
|
||||||
if (!already_divided) {
|
|
||||||
insert_new(meta_item);
|
|
||||||
already_divided = true;
|
|
||||||
} else {
|
|
||||||
do_insert_item(meta_item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void on_upper_notify() {
|
private void on_upper_notify() {
|
||||||
if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size
|
if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size
|
||||||
if (at_current_content) {
|
if (at_current_content) {
|
||||||
|
|
Loading…
Reference in a new issue