Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits: 7cf38b19 by wurstsalat at 2021-02-14T14:26:35+01:00 Add Workspace menu
- - - - - a442e375 by wurstsalat at 2021-02-14T14:42:01+01:00 WorkspaceSidebar: Add drag and drop
- - - - -
4 changed files:
- gajim/data/gui/main.ui - gajim/data/style/gajim.css - gajim/gtk/main.py - gajim/gtk/workspace_side_bar.py
Changes:
===================================== gajim/data/gui/main.ui ===================================== @@ -175,28 +175,25 @@ </packing> </child> <child> - <object class="GtkButton" id="edit_workspace_button"> + <object class="GtkMenuButton" id="workspace_menu_button"> <property name="visible">True</property> <property name="can-focus">True</property> + <property name="focus-on-click">False</property> <property name="receives-default">True</property> - <property name="tooltip-text" translatable="yes">Edit Workspace</property> - <property name="relief">none</property> + <property name="use-popover">False</property> <child> <object class="GtkImage"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">document-edit-symbolic</property> + <property name="icon-name">open-menu-symbolic</property> </object> </child> - <style> - <class name="flat"/> - </style> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="pack-type">end</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </object>
===================================== gajim/data/style/gajim.css ===================================== @@ -61,12 +61,12 @@ /* Workspace sidebar */ .workspace-sidebar { background: transparent; - margin-left: 12px; + margin-left: 6px; margin-right: 6px; } .workspace-sidebar-item { padding-top: 0px; - padding-bottom: 6px; + padding-bottom: 0px; } .workspace-sidebar-item:selected, .workspace-sidebar-item:hover { @@ -80,8 +80,13 @@ } .workspace-sidebar-item image { border-radius: 9px; - margin-top: 6px; - margin-right: 6px; + margin: 6px; +} +.workspace-sidebar-item.drag-hover-top { + border-top: 1px solid #4e9a06; +} +.workspace-sidebar-item.drag-hover-bottom { + border-bottom: 1px solid #4e9a06; }
/* Account sidebar */
===================================== gajim/gtk/main.py ===================================== @@ -27,6 +27,11 @@
log = logging.getLogger('gajim.gui.main')
+WORKSPACE_MENU_DICT = { + 'edit': _('Edit…'), + 'remove': _('Remove'), +} +
class MainWindow(Gtk.ApplicationWindow, EventHelper): def __init__(self): @@ -64,8 +69,12 @@ def __init__(self):
self._ui.right_grid.add(self._chat_stack)
- self._ui.edit_workspace_button.connect( - 'clicked', self._on_edit_workspace_clicked) + workspace_menu = Gio.Menu() + for action, label in WORKSPACE_MENU_DICT.items(): + workspace_menu.append(label, f'win.{action.lower()}-workspace') + + self._ui.workspace_menu_button.set_menu_model(workspace_menu) + self._ui.start_chat_button.connect( 'clicked', self._on_start_chat_clicked) self._ui.connect_signals(self) @@ -146,7 +155,8 @@ def _on_button_release(paned, event): def _add_actions(self): actions = [ ('add-workspace', 's', self._add_workspace), - ('remove-workspace', 's', self._remove_workspace), + ('edit-workspace', None, self._edit_workspace), + ('remove-workspace', None, self._remove_workspace), ('activate-workspace', 's', self._activate_workspace), ('add-chat', 'as', self._add_chat), ('add-group-chat', 'as', self._add_group_chat), @@ -155,7 +165,8 @@ def _add_actions(self):
for action in actions: action_name, variant, func = action - variant = GLib.VariantType.new(variant) + if variant is not None: + variant = GLib.VariantType.new(variant) act = Gio.SimpleAction.new(action_name, variant) act.connect('activate', func) self.add_action(act) @@ -311,9 +322,19 @@ def add_workspace(self, workspace_id): self._workspace_side_bar.activate_workspace(workspace_id) self._chat_list_stack.show_chat_list(workspace_id)
- def _remove_workspace(self, _action, param): - workspace_id = param.get_string() - self.remove_workspace(workspace_id) + def _edit_workspace(self, _action, _param): + workspace_id = self.get_active_workspace() + if workspace_id is not None: + self.edit_workspace(workspace_id) + + @staticmethod + def edit_workspace(workspace_id): + open_window('WorkspaceDialog', workspace_id=workspace_id) + + def _remove_workspace(self, _action, _param): + workspace_id = self.get_active_workspace() + if workspace_id is not None: + self.remove_workspace(workspace_id)
def remove_workspace(self, workspace_id): was_active = self.get_active_workspace() == workspace_id @@ -452,10 +473,6 @@ def _load_chats(self): def _on_start_chat_clicked(self, _button): app.app.activate_action('start-chat', GLib.Variant('s', ''))
- def _on_edit_workspace_clicked(self, _button): - open_window('WorkspaceDialog', - workspace_id=self.get_active_workspace()) - def _on_event(self, event): if event.name == 'caps-update': #TODO
===================================== gajim/gtk/workspace_side_bar.py ===================================== @@ -1,9 +1,10 @@
+from gi.repository import Gdk from gi.repository import Gtk from gi.repository import GLib -from gajim.common.const import AvatarSize -from gajim.common import app
+from gajim.common import app +from gajim.common.const import AvatarSize from gajim.common.i18n import _
from .util import open_window @@ -14,18 +15,119 @@ def __init__(self, chat_list_stack): Gtk.ListBox.__init__(self) self.set_vexpand(True) self.set_valign(Gtk.Align.START) - self.set_sort_func(self._sort_func) self.get_style_context().add_class('workspace-sidebar') - self.add(AddWorkspace('add')) - self.connect('button-press-event', self._on_button_press) + + # Drag and Drop + entries = [Gtk.TargetEntry.new( + 'WORKSPACE_SIDEBAR_ITEM', + Gtk.TargetFlags.SAME_APP, + 0)] + self.drag_dest_set( + Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP, + entries, + Gdk.DragAction.MOVE) + + self.drag_row = None + self.row_before = None + self.row_after = None + + self.connect('drag-motion', self._on_drag_motion) + self.connect('drag-data-received', self._on_drag_data_received) + self.connect('drag-leave', self._on_drag_leave) + self.connect('row-activated', self._on_row_activated)
+ self.add(AddWorkspace('add')) + chat_list_stack.connect('unread-count-changed', self._on_unread_count_changed) chat_list_stack.connect('chat-selected', self._on_chat_selected) self._workspaces = {}
+ def _on_drag_motion(self, _widget, _drag_context, _x_coord, y_coord, + _time): + row = self.get_row_at_y(y_coord) + + if self.row_before: + self.row_before.get_style_context().remove_class( + 'drag-hover-bottom') + if self.row_after: + self.row_after.get_style_context().remove_class( + 'drag-hover-top') + + if row: + alloc = row.get_allocation() + hover_row_y = alloc.y + hover_row_height = alloc.height + if y_coord < hover_row_y + hover_row_height / 2: + self.row_after = row + self.row_before = self._get_row_before(row) + else: + self.row_before = row + self.row_after = self._get_row_after(row) + else: + self.row_before = self._get_last_workspace_row() + self.row_after = None + + if self.drag_row in (self.row_before, self.row_after): + return False + + if self.row_before and self.row_before.workspace_id != 'add': + self.row_before.get_style_context().add_class('drag-hover-bottom') + if self.row_after: + self.row_after.get_style_context().add_class('drag-hover-top') + return True + + def _on_drag_data_received(self, _widget, _drag_context, _x_coord, + _y_coord, selection_data, _info, _time): + if self.row_before: + self.row_before.get_style_context().remove_class( + 'drag-hover-bottom') + if self.row_after: + self.row_after.get_style_context().remove_class( + 'drag-hover-top') + + data = selection_data.get_data() + workspace_id = data.decode('utf-8') + row = self.get_workspace_by_id(workspace_id) + + if row in (self.row_after, None): + return + + self.remove(row) + + if self.row_after: + pos = self.row_after.get_index() + elif self.row_before and self.row_before.workspace_id != 'add': + pos = self.row_before.get_index() + 1 + else: + pos = len(self.get_children()) - 1 + + self.insert(row, pos) + app.window.activate_workspace(workspace_id) + + def _on_drag_leave(self, _widget, _drag_context, _time): + if self.row_before: + self.row_before.get_style_context().remove_class( + 'drag-hover-bottom') + if self.row_after: + self.row_after.get_style_context().remove_class( + 'drag-hover-top') + + def _get_row_before(self, row): + return self.get_row_at_index(row.get_index() - 1) + + def _get_row_after(self, row): + return self.get_row_at_index(row.get_index() + 1) + + def _get_last_workspace_row(self): + # len(children) would include AddWorkspace + return self.get_row_at_index(len(self.get_children()) - 1) + + def set_drag_row(self, row): + self.drag_row = row + def _on_unread_count_changed(self, _chat_list_stack, workspace_id, count): workspace = self._workspaces[workspace_id] workspace.set_unread_count(count) @@ -33,21 +135,6 @@ def _on_unread_count_changed(self, _chat_list_stack, workspace_id, count): def _on_chat_selected(self, _chat_list_stack, workspace_id, *args): self.activate_workspace(workspace_id)
- @staticmethod - def _sort_func(child1, child2): - if child1.sort_index < child2.sort_index: - return -1 - - if child1.sort_index == child2.sort_index: - return 0 - return 1 - - def _on_button_press(self, _listbox, button_event): - if button_event.button != 3: - return - row = self.get_row_at_y(button_event.y) - WorspaceMenu(row) - @staticmethod def _on_row_activated(_listbox, row): if row.workspace_id == 'add': @@ -58,7 +145,8 @@ def _on_row_activated(_listbox, row): def add_workspace(self, workspace_id): row = Workspace(workspace_id) self._workspaces[workspace_id] = row - self.add(row) + # Insert row before AddWorkspace row + self.insert(row, len(self.get_children()) - 1)
def remove_workspace(self, workspace_id): if len(self._workspaces) == 1: @@ -85,6 +173,12 @@ def get_first_workspace(self): for row in self.get_children(): return row.workspace_id
+ def get_workspace_by_id(self, workspace_id): + for row in self.get_children(): + if row.workspace_id == workspace_id: + return row + return None + def update_avatar(self, workspace_id): row = self._workspaces[workspace_id] row.update_avatar() @@ -102,8 +196,6 @@ class Workspace(CommonWorkspace): def __init__(self, workspace_id): CommonWorkspace.__init__(self, workspace_id)
- self.sort_index = 0 - self._unread_label = Gtk.Label() self._unread_label.get_style_context().add_class( 'unread-counter') @@ -118,7 +210,21 @@ def __init__(self, workspace_id): overlay.add(self._image) overlay.add_overlay(self._unread_label)
- self.add(overlay) + # Drag and Drop + entries = [Gtk.TargetEntry.new( + 'WORKSPACE_SIDEBAR_ITEM', + Gtk.TargetFlags.SAME_APP, + 0)] + eventbox = Gtk.EventBox() + eventbox.drag_source_set( + Gdk.ModifierType.BUTTON1_MASK, + entries, + Gdk.DragAction.MOVE) + eventbox.connect('drag-begin', self._on_drag_begin) + eventbox.connect('drag-data-get', self._on_drag_data_get) + eventbox.add(overlay) + + self.add(eventbox) self.show_all()
def update_avatar(self): @@ -131,15 +237,27 @@ def set_unread_count(self, count): self._unread_label.set_text('999+') self._unread_label.set_visible(bool(count))
+ def _on_drag_begin(self, _widget, drag_context): + scale = self.get_scale_factor() + surface = app.interface.avatar_storage.get_workspace_surface( + self.workspace_id, AvatarSize.WORKSPACE, scale) + Gtk.drag_set_icon_surface(drag_context, surface) + + listbox = self.get_parent() + listbox.set_drag_row(self) + + def _on_drag_data_get(self, _widget, _drag_context, selection_data, + _info, _time): + drop_type = Gdk.Atom.intern_static_string('WORKSPACE_SIDEBAR_ITEM') + data = self.workspace_id.encode('utf-8') + selection_data.set(drop_type, 32, data) +
class AddWorkspace(CommonWorkspace): def __init__(self, workspace_id): CommonWorkspace.__init__(self, workspace_id) - self.set_selectable(False)
- self.sort_index = 10000 - image = Gtk.Image.new_from_icon_name('list-add-symbolic', Gtk.IconSize.DND) image.set_halign(Gtk.Align.CENTER) @@ -148,33 +266,6 @@ def __init__(self, workspace_id): self.show_all()
-class WorspaceMenu(Gtk.Popover): - def __init__(self, row): - Gtk.Popover.__init__(self) - entries = [ - (_('Remove'), 'remove-workspace'), - ] - - self._workspace_id = row.workspace_id - - box = Gtk.Box() - for label, action in entries: - button = Gtk.Button(label=label) - button.connect('clicked', self._on_click, action) - box.add(button) - box.show_all() - - self.add(box) - self.set_relative_to(row) - self.popup() - - def _on_click(self, _button, action): - self.popdown() - self.set_relative_to(None) - app.window.activate_action(action, - GLib.Variant('s', self._workspace_id)) - - class WorkspaceAvatar(Gtk.Image): def __init__(self, workspace_id): Gtk.Image.__init__(self)
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/74ae244abc140baa70cef82894294d5a...