Merge branch 'master' into 'master'

Refactor some Tooltips

See merge request gajim/gajim!180
This commit is contained in:
Philipp Hörist 2017-12-24 08:40:04 +01:00
commit fd7f302044
7 changed files with 109 additions and 287 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<!-- Generated with glade 3.20.1 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkAccelGroup" id="accelgroup1"/>
@ -101,8 +101,6 @@
<signal name="button-press-event" handler="on_transfers_list_button_press_event" swapped="no"/>
<signal name="button-release-event" handler="on_transfers_list_button_release_event" swapped="no"/>
<signal name="key-press-event" handler="on_transfers_list_key_press_event" swapped="no"/>
<signal name="leave-notify-event" handler="on_transfers_list_leave_notify_event" swapped="no"/>
<signal name="motion-notify-event" handler="on_transfers_list_motion_notify_event" swapped="no"/>
<signal name="row-activated" handler="on_transfers_list_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
@ -247,6 +245,9 @@
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="file_transfers_window-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">File Transfers</property>

View File

@ -140,7 +140,12 @@ class FileTransfersWindow:
self.tree.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
self.tree.get_selection().connect('changed', self.selection_changed)
# Tooltip
self.tree.connect('query-tooltip', self._query_tooltip)
self.tree.set_has_tooltip(True)
self.tooltip = tooltips.FileTransfersTooltip()
self.file_transfers_menu = self.xml.get_object('file_transfers_menu')
self.open_folder_menuitem = self.xml.get_object('open_folder_menuitem')
self.cancel_menuitem = self.xml.get_object('cancel_menuitem')
@ -149,6 +154,33 @@ class FileTransfersWindow:
self.remove_menuitem = self.xml.get_object('remove_menuitem')
self.xml.connect_signals(self)
def _query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip):
try:
x_pos, y_pos = widget.convert_widget_to_bin_window_coords(
x_pos, y_pos)
row = widget.get_path_at_pos(x_pos, y_pos)[0]
except TypeError:
self.tooltip.clear_tooltip()
return False
if not row:
self.tooltip.clear_tooltip()
return False
iter_ = None
try:
model = widget.get_model()
iter_ = model.get_iter(row)
except Exception:
self.tooltip.clear_tooltip()
return False
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
value, widget = self.tooltip.get_tooltip(file_props, sid)
tooltip.set_custom(widget)
return value
def find_transfer_by_jid(self, account, jid):
"""
Find all transfers with peer 'jid' that belong to 'account'
@ -301,9 +333,7 @@ class FileTransfersWindow:
def on_ok(widget):
file_dir = None
files_path_list = dialog.get_filenames()
text_buffer = desc_entry.get_buffer()
desc = text_buffer.get_text(text_buffer.get_start_iter(),
text_buffer.get_end_iter(), True)
desc = desc_entry.get_text()
for file_path in files_path_list:
if self.send_file(account, contact, file_path, desc) \
and file_dir is None:
@ -721,7 +751,6 @@ class FileTransfersWindow:
"""
Add new transfer to FT window and show the FT window
"""
self.on_transfers_list_leave_notify_event(None)
if file_props is None:
return
file_props.elapsed_time = 0
@ -752,45 +781,6 @@ class FileTransfersWindow:
self.set_cleanup_sensitivity()
self.window.show_all()
def on_transfers_list_motion_notify_event(self, widget, event):
w = self.tree.get_window()
device = w.get_display().get_device_manager().get_client_pointer()
pointer = w.get_device_position(device)
props = widget.get_path_at_pos(int(event.x), int(event.y))
self.height_diff = pointer[2] - int(event.y)
if self.tooltip.timeout > 0 or self.tooltip.shown:
if not props or self.tooltip.id != props[0]:
self.tooltip.hide_tooltip()
if props:
row = props[0]
iter_ = None
try:
iter_ = self.model.get_iter(row)
except Exception:
self.tooltip.hide_tooltip()
return
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
if file_props is not None:
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
self.tooltip.id = row
self.tooltip.timeout = GLib.timeout_add(500,
self.show_tooltip, widget)
def on_transfers_list_leave_notify_event(self, widget=None, event=None):
if event is not None:
self.height_diff = int(event.y)
elif self.height_diff is 0:
return
w = self.tree.get_window()
device = w.get_display().get_device_manager().get_client_pointer()
pointer = w.get_device_position(device)
props = self.tree.get_path_at_pos(pointer[1],
pointer[2] - self.height_diff)
if self.tooltip.timeout > 0 or self.tooltip.shown:
if not props or self.tooltip.id == props[0]:
self.tooltip.hide_tooltip()
def on_transfers_list_row_activated(self, widget, path, col):
# try to open the containing folder
self.on_open_folder_menuitem_activate(widget)
@ -954,37 +944,11 @@ class FileTransfersWindow:
con.disconnect_transfer(file_props)
self.set_status(file_props, 'stop')
def show_tooltip(self, widget):
self.tooltip.timeout = 0
if self.height_diff == 0:
self.tooltip.hide_tooltip()
return
w = self.tree.get_window()
device = w.get_display().get_device_manager().get_client_pointer()
pointer = w.get_device_position(device)
props = self.tree.get_path_at_pos(pointer[1],
pointer[2] - self.height_diff)
# check if the current pointer is at the same path
# as it was before setting the timeout
if props and self.tooltip.id == props[0]:
iter_ = self.model.get_iter(props[0])
sid = self.model[iter_][Column.SID]
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
# bounding rectangle of coordinates for the cell within the treeview
rect = self.tree.get_cell_area(props[0], props[1])
# position of the treeview on the screen
position = widget.get_window().get_origin()[1:]
self.tooltip.show_tooltip(file_props, rect.height,
position[1] + rect.y + self.height_diff)
else:
self.tooltip.hide_tooltip()
def on_notify_ft_complete_checkbox_toggled(self, widget):
app.config.set('notify_on_file_complete',
widget.get_active())
def on_file_transfers_dialog_delete_event(self, widget, event):
self.on_transfers_list_leave_notify_event(widget, None)
self.window.hide()
return True # do NOT destory window
@ -1006,7 +970,6 @@ class FileTransfersWindow:
"""
When a key is pressed in the treeviews
"""
self.tooltip.hide_tooltip()
iter_ = None
try:
iter_ = self.tree.get_selection().get_selected()[1]
@ -1024,7 +987,6 @@ class FileTransfersWindow:
def on_transfers_list_button_release_event(self, widget, event):
# hide tooltip, no matter the button is pressed
self.tooltip.hide_tooltip()
path = None
try:
path = self.tree.get_path_at_pos(int(event.x), int(event.y))[0]
@ -1037,7 +999,6 @@ class FileTransfersWindow:
def on_transfers_list_button_press_event(self, widget, event):
# hide tooltip, no matter the button is pressed
self.tooltip.hide_tooltip()
path, iter_ = None, None
try:
path = self.tree.get_path_at_pos(int(event.x), int(event.y))[0]

View File

@ -303,7 +303,6 @@ class GroupchatControl(ChatControlBase):
formattings_button = self.xml.get_object('formattings_button')
formattings_button.set_sensitive(False)
self.current_tooltip = None
if parent_win is not None:
# On AutoJoin with minimize Groupchats are created without parent
# Tooltip Window and Actions have to be created with parent
@ -474,6 +473,9 @@ class GroupchatControl(ChatControlBase):
self.banner_actionbar.pack_end(self.hide_roster_button)
self.banner_actionbar.pack_start(self.subject_button)
# GC Roster tooltip
self.gc_tooltip = tooltips.GCTooltip()
self.control_menu = gui_menu_builder.get_groupchat_menu(self.control_id)
app.ged.register_event_handler('gc-presence-received', ged.GUI1,
@ -714,8 +716,6 @@ class GroupchatControl(ChatControlBase):
if widget.get_tooltip_window():
return
widget.set_has_tooltip(True)
widget.set_tooltip_window(tooltips.GCTooltip(
self.account, self.parent_win.window))
id_ = widget.connect('query-tooltip', self.query_tooltip)
self.handlers[id_] = widget
@ -723,41 +723,35 @@ class GroupchatControl(ChatControlBase):
try:
row = self.list_treeview.get_path_at_pos(x_pos, y_pos)[0]
except TypeError:
self.gc_tooltip.clear_tooltip()
return False
if not row:
self.gc_tooltip.clear_tooltip()
return False
iter_ = None
try:
iter_ = self.model.get_iter(row)
except Exception:
self.gc_tooltip.clear_tooltip()
return False
typ = self.model[iter_][Column.TYPE]
nick = self.model[iter_][Column.NICK]
if typ != 'contact':
self.gc_tooltip.clear_tooltip()
return False
if self.current_tooltip != row:
# If the row changes we hide the current tooltip
self.current_tooltip = row
return False
tooltip = widget.get_tooltip_window()
if tooltip.row == row:
# We already populated the window with the row data
return True
tooltip.row = row
contact = app.contacts.get_gc_contact(
self.account, self.room_jid, nick)
if not contact:
self.gc_tooltip.clear_tooltip()
return False
tooltip.populate(contact)
return True
value, widget = self.gc_tooltip.get_tooltip(contact)
tooltip.set_custom(widget)
return value
def fill_column(self, col):
for rend in self.renderers_list:

View File

@ -1102,8 +1102,6 @@ if __name__ == '__main__':
htmlview = ConversationTextview(None)
tooltip = tooltips.BaseTooltip()
def on_textview_motion_notify_event(widget, event):
"""
Change the cursor to a hand when we are over a mail or an url
@ -1129,17 +1127,6 @@ if __name__ == '__main__':
except Exception:
pass
#if line_tooltip.timeout != 0:
# Check if we should hide the line tooltip
# if not over_line:
# line_tooltip.hide_tooltip()
#if over_line and not line_tooltip.win:
# line_tooltip.timeout = GLib.timeout_add(500,
# show_line_tooltip)
# htmlview.tv.get_window(Gtk.TextWindowType.TEXT).set_cursor(
# gtkgui_helpers.get_cursor('LEFT_PTR'))
# change_cursor = tag
htmlview.tv.connect('motion_notify_event', on_textview_motion_notify_event)
def handler(texttag, widget, event, iter_, kind):

View File

@ -102,6 +102,15 @@ class MessageTextView(Gtk.TextView):
text = buf.get_text(start, end, True)
return text != self.PLACEHOLDER and text != ''
def get_text(self):
# gets the text if its not PLACEHOLDER
buf = self.get_buffer()
start, end = buf.get_bounds()
text = self.get_buffer().get_text(start, end, True)
if text == self.PLACEHOLDER:
return ''
return text
def is_placeholder(self):
buf = self.get_buffer()
start, end = buf.get_bounds()

View File

@ -104,8 +104,7 @@ class StatusIcon:
self.make_menu(event_button, event_time)
def on_status_icon_query_tooltip(self, widget, x, y, keyboard_mode, tooltip):
self.tooltip.populate()
tooltip.set_custom(self.tooltip.hbox)
tooltip.set_custom(self.tooltip.get_tooltip())
return True
def hide_icon(self):

View File

@ -28,14 +28,14 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import os
import time
from datetime import datetime
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
import os
import time
from datetime import datetime
from datetime import timedelta
from gajim import gtkgui_helpers
from gajim.common.const import AvatarSize
@ -43,147 +43,6 @@ from gajim.common import app
from gajim.common import helpers
from gajim.common.i18n import Q_
class BaseTooltip:
"""
Base Tooltip class
Usage:
tooltip = BaseTooltip()
....
tooltip.show_tooltip(data, widget_height, widget_y_position)
....
if tooltip.timeout != 0:
tooltip.hide_tooltip()
* data - the text to be displayed (extenders override this argument and
display more complex contents)
* widget_height - the height of the widget on which we want to show tooltip
* widget_y_position - the vertical position of the widget on the screen
Tooltip is displayed aligned centered to the mouse poiner and 4px below the widget.
In case tooltip goes below the visible area it is shown above the widget.
"""
def __init__(self):
self.timeout = 0
self.preferred_position = [0, 0]
self.win = None
self.id = None
self.cur_data = None
self.shown = False
self.position_computed = False
def populate(self, data):
"""
This method must be overriden by all extenders. This is the most simple
implementation: show data as value of a label
"""
self.create_window()
self.win.add(Gtk.Label(label=data))
def create_window(self):
"""
Create a popup window each time tooltip is requested
"""
self.win = Gtk.Window.new(Gtk.WindowType.POPUP)
self.win.set_title('tooltip')
self.win.set_border_width(3)
self.win.set_resizable(False)
self.win.set_name('gtk-tooltips')
self.win.set_type_hint(Gdk.WindowTypeHint.TOOLTIP)
self.win.set_events(Gdk.EventMask.POINTER_MOTION_MASK)
self.win.connect('size-allocate', self.on_size_allocate)
self.win.connect('motion-notify-event', self.motion_notify_event)
self.screen = self.win.get_screen()
def _get_icon_name_for_tooltip(self, contact):
"""
Helper function used for tooltip contacts/acounts
Tooltip on account has fake contact with sub == '', in this case we show
real status of the account
"""
if contact.ask == 'subscribe':
return 'requested'
elif contact.sub in ('both', 'to', ''):
return contact.show
return 'not in roster'
def motion_notify_event(self, widget, event):
GLib.idle_add(self.hide_tooltip)
def on_size_allocate(self, widget, rect):
if not self.position_computed:
half_width = rect.width / 2 + 1
if self.preferred_position[1] + rect.height > \
self.screen.get_height():
# flip tooltip up
self.preferred_position[1] -= rect.height + self.widget_height \
+ 8
if self.preferred_position[1] < 0:
self.preferred_position[1] = self.screen.get_height() - \
rect.height - 2
if self.preferred_position[0] + rect.width + 7 < \
self.screen.get_width():
self.preferred_position[0] = self.preferred_position[0]\
+ 7
else:
self.preferred_position[0] = self.preferred_position[0]\
- rect.width - 7
self.win.move(self.preferred_position[0],
self.preferred_position[1])
return
if self.preferred_position[0] < half_width:
self.preferred_position[0] = 0
elif self.preferred_position[0] + rect.width > \
self.screen.get_width() + half_width:
self.preferred_position[0] = self.screen.get_width() - \
rect.width
else:
self.preferred_position[0] -= half_width
self.position_computed = True
self.win.move(self.preferred_position[0], self.preferred_position[1])
def show_tooltip(self, data, widget_height, widget_y_position):
"""
Show tooltip on widget
Data contains needed data for tooltip contents.
widget_height is the height of the widget on which we show the tooltip.
widget_y_position is vertical position of the widget on the screen.
"""
if self.shown:
return
self.position_computed = False
self.cur_data = data
# set tooltip contents
self.populate(data)
# get the X position of mouse pointer on the screen
pointer_x = self.screen.get_display().get_device_manager().\
get_client_pointer().get_position()[1]
# get the prefered X position of the tooltip on the screen in case this position is >
# than the height of the screen, tooltip will be shown above the widget
preferred_y = widget_y_position + widget_height + 4
self.preferred_position = [pointer_x, preferred_y]
self.widget_height = widget_height
self.win.show_all()
self.shown = True
def hide_tooltip(self):
if self.timeout > 0:
GLib.source_remove(self.timeout)
self.timeout = 0
if self.win:
self.win.destroy()
self.win = None
self.id = None
self.cur_data = None
self.shown = False
class StatusTable:
"""
@ -263,13 +122,13 @@ class StatusTable:
self.table.attach(lock_image, 4, self.current_row, 1, 1)
self.current_row += 1
class NotificationAreaTooltip(BaseTooltip, StatusTable):
class NotificationAreaTooltip(StatusTable):
"""
Tooltip that is shown in the notification area
"""
def __init__(self):
BaseTooltip.__init__(self)
StatusTable.__init__(self)
def fill_table_with_accounts(self, accounts):
@ -297,8 +156,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
for line in acct['event_lines']:
self.add_text_row(' ' + line, 1)
def populate(self, data=''):
self.create_window()
def get_tooltip(self):
self.create_table()
accounts = helpers.get_notification_icon_tooltip_dict()
@ -308,40 +166,43 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
self.hbox.add(self.table)
self.hbox.show_all()
return self.hbox
class GCTooltip(Gtk.Window):
class GCTooltip():
# pylint: disable=E1101
def __init__(self, account, parent):
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP, transient_for=parent)
self.account = account
self.row = None
self.set_title('tooltip')
self.set_border_width(3)
self.set_resizable(False)
self.set_name('gtk-tooltips')
self.set_type_hint(Gdk.WindowTypeHint.TOOLTIP)
def __init__(self):
self.contact = None
self.xml = gtkgui_helpers.get_gtk_builder('tooltip_gc_contact.ui')
for name in ('nick', 'status', 'jid', 'user_show', 'fillelement',
'resource', 'affiliation', 'avatar', 'resource_label',
'jid_label', 'tooltip_grid'):
'resource', 'affiliation', 'avatar', 'resource_label',
'jid_label', 'tooltip_grid'):
setattr(self, name, self.xml.get_object(name))
self.add(self.tooltip_grid)
self.tooltip_grid.show()
def clear_tooltip(self):
self.contact = None
def get_tooltip(self, contact):
if self.contact == contact:
return True, self.tooltip_grid
self._populate_grid(contact)
self.contact = contact
return False, self.tooltip_grid
def _hide_grid_childs(self):
"""
Hide all Elements of the Tooltip Grid
"""
for child in self.tooltip_grid.get_children():
child.hide()
def populate(self, contact):
def _populate_grid(self, contact):
"""
Populate the Tooltip Grid with data of from the contact
"""
self.clear_tooltip()
self._hide_grid_childs()
self.nick.set_text(contact.get_shown_name())
self.nick.show()
@ -750,19 +611,28 @@ class RosterTooltip(Gtk.Window, StatusTable):
return 'not in roster'
class FileTransfersTooltip(BaseTooltip):
"""
Tooltip that is shown in the notification area
"""
class FileTransfersTooltip():
def __init__(self):
BaseTooltip.__init__(self)
self.sid = None
self.widget = None
def populate(self, file_props):
def clear_tooltip(self):
self.sid = None
self.widget = None
def get_tooltip(self, file_props, sid):
if self.sid == sid:
return True, self.widget
self.widget = self._create_tooltip(file_props, sid)
self.sid = sid
return False, self.widget
@staticmethod
def _create_tooltip(file_props, sid):
ft_table = Gtk.Table(2, 1)
ft_table.set_property('column-spacing', 2)
current_row = 1
self.create_window()
properties = []
name = file_props.name
if file_props.type_ == 'r':
@ -794,18 +664,18 @@ class FileTransfersTooltip(BaseTooltip):
status = ''
if file_props.started:
status = _('Not started')
if file_props.stopped == True:
if file_props.stopped:
status = _('Stopped')
elif file_props.completed:
status = _('Completed')
elif file_props.connected == False:
elif not file_props.connected:
if file_props.completed:
status = _('Completed')
else:
if file_props.paused == True:
if file_props.paused:
status = Q_('?transfer status:Paused')
elif file_props.stalled == True:
#stalled is not paused. it is like 'frozen' it stopped alone
elif file_props.stalled:
# stalled is not paused. it is like 'frozen' it stopped alone
status = _('Stalled')
else:
status = _('Transferring')
@ -832,7 +702,8 @@ class FileTransfersTooltip(BaseTooltip):
ft_table.attach(label, 2, 3, current_row, current_row + 1,
Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0)
self.win.add(ft_table)
ft_table.show_all()
return ft_table
def colorize_status(status):