From b8aaf09c13253a2a5101d9478dbe972581953d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=B6rist?= Date: Tue, 18 Jul 2017 00:53:50 +0200 Subject: [PATCH] Fix infinite expand of MessageTextView - The 'configure-event' does not trigger anymore when connected to the MessageTextView. The reason is unknown. - Use our own ScrolledWindow Widget instead so we can better control the dimension of the MessageTextView --- data/gui/chat_control.ui | 16 +------ gajim/chat_control_base.py | 75 +++++++------------------------- gajim/message_textview.py | 12 ++++- gajim/scrolled_window.py | 89 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 76 deletions(-) create mode 100644 gajim/scrolled_window.py diff --git a/data/gui/chat_control.ui b/data/gui/chat_control.ui index 85d21fadc..bab728ca0 100644 --- a/data/gui/chat_control.ui +++ b/data/gui/chat_control.ui @@ -629,21 +629,7 @@ - - True - True - never - never - in - - - - - - True - True - 1 - + diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py index bbff1a585..9162c769b 100644 --- a/gajim/chat_control_base.py +++ b/gajim/chat_control_base.py @@ -44,6 +44,7 @@ import notify import re import emoticons +from scrolled_window import ScrolledWindow from common import events from common import gajim from common import helpers @@ -327,15 +328,21 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.last_received_id = {} # one per name # add MessageTextView to UI and connect signals - self.msg_scrolledwindow = self.xml.get_object('message_scrolledwindow') self.msg_textview = MessageTextView() + + self.msg_scrolledwindow = ScrolledWindow() + self.msg_scrolledwindow.set_max_content_height(100) + self.msg_scrolledwindow.set_min_content_height(23) + + self.msg_scrolledwindow.set_property('shadow_type', Gtk.ShadowType.IN) self.msg_scrolledwindow.add(self.msg_textview) + + hbox = self.xml.get_object('hbox') + hbox.pack_end(self.msg_scrolledwindow, True, True, 0) + id_ = self.msg_textview.connect('key_press_event', self._on_message_textview_key_press_event) self.handlers[id_] = self.msg_textview - id_ = self.msg_textview.connect('configure-event', - self.on_configure_event) - self.handlers[id_] = self.msg_textview id_ = self.msg_textview.connect('populate_popup', self.on_msg_textview_populate_popup) self.handlers[id_] = self.msg_textview @@ -369,8 +376,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): # For XEP-0172 self.user_nick = None - self.smooth = True - self.command_hits = [] self.last_key_tabs = False @@ -693,6 +698,10 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.send_message(message, xhtml=xhtml) else: message_buffer.insert_at_cursor('\n') + mark = message_buffer.get_insert() + iter_ = message_buffer.get_iter_at_mark(mark) + if message_buffer.get_end_iter().equal(iter_): + GLib.idle_add(gtkgui_helpers.scroll_to_end, self.msg_scrolledwindow) return True elif event.keyval == Gdk.KEY_z: # CTRL+z @@ -1157,60 +1166,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.conv_textview.scroll_to_end_iter() return False - def on_configure_event(self, msg_textview, event): - """ - When message_textview changes its size: if the new height will enlarge - the window, enable the scrollbar automatic policy. Also enable scrollbar - automatic policy for horizontal scrollbar if message we have in - message_textview is too big - """ - if msg_textview.get_window() is None: - return - - min_height = self.conv_scrolledwindow.get_property('height-request') - conversation_height = self.conv_textview.tv.get_window().get_size()[1] - message_height = msg_textview.get_window().get_size()[1] - message_width = msg_textview.get_window().get_size()[0] - # new tab is not exposed yet - if conversation_height < 2: - return - - if conversation_height < min_height: - min_height = conversation_height - - # we don't want to always resize in height the message_textview - # so we have minimum on conversation_textview's scrolled window - # but we also want to avoid window resizing so if we reach that - # minimum for conversation_textview and maximum for message_textview - # we set to automatic the scrollbar policy - diff_y = message_height - event.height - if diff_y != 0: - if conversation_height + diff_y < min_height: - if message_height + conversation_height - min_height > min_height: - policy = self.msg_scrolledwindow.get_property( - 'vscrollbar-policy') - if policy != Gtk.PolicyType.AUTOMATIC: - self.msg_scrolledwindow.set_property('vscrollbar-policy', - Gtk.PolicyType.AUTOMATIC) - self.msg_scrolledwindow.set_property('height-request', - message_height + conversation_height - min_height) - else: - self.msg_scrolledwindow.set_property('vscrollbar-policy', - Gtk.PolicyType.NEVER) - self.msg_scrolledwindow.set_property('height-request', -1) - - self.smooth = True # reinit the flag - # enable scrollbar automatic policy for horizontal scrollbar - # if message we have in message_textview is too big - if event.width > message_width: - self.msg_scrolledwindow.set_property('hscrollbar-policy', - Gtk.PolicyType.AUTOMATIC) - else: - self.msg_scrolledwindow.set_property('hscrollbar-policy', - Gtk.PolicyType.NEVER) - - return True - def on_conversation_vadjustment_changed(self, adjustment): # used to stay at the end of the textview when we shrink conversation # textview. diff --git a/gajim/message_textview.py b/gajim/message_textview.py index 83900df6e..9bc440670 100644 --- a/gajim/message_textview.py +++ b/gajim/message_textview.py @@ -29,6 +29,7 @@ from gi.repository import GLib from gi.repository import Pango from common import gajim +import gtkgui_helpers class MessageTextView(Gtk.TextView): """ @@ -38,7 +39,7 @@ class MessageTextView(Gtk.TextView): UNDO_LIMIT = 20 def __init__(self): - GObject.GObject.__init__(self) + Gtk.TextView.__init__(self) # set properties self.set_border_width(1) @@ -79,6 +80,15 @@ class MessageTextView(Gtk.TextView): self.begin_tags['strike'] = '' self.end_tags['strike'] = '' + self.connect_after('paste-clipboard', self.after_paste_clipboard) + + def after_paste_clipboard(self, textview): + buffer_ = textview.get_buffer() + mark = buffer_.get_insert() + iter_ = buffer_.get_iter_at_mark(mark) + if iter_.get_offset() == buffer_.get_end_iter().get_offset(): + GLib.idle_add(gtkgui_helpers.scroll_to_end, textview.get_parent()) + def make_clickable_urls(self, text): _buffer = self.get_buffer() diff --git a/gajim/scrolled_window.py b/gajim/scrolled_window.py new file mode 100644 index 000000000..9b7392275 --- /dev/null +++ b/gajim/scrolled_window.py @@ -0,0 +1,89 @@ +# -*- coding:utf-8 -*- +# Copyright (C) 2015 Patrick Griffis +# Copyright (C) 2014 Christian Hergert +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . + +from gi.repository import GObject, Gtk + +class ScrolledWindow(Gtk.ScrolledWindow): + """ + ScrolledWindow that sets a max size for the child to grow into. + Taken from the Gnome Builder project: + https://git.gnome.org/browse/gnome-builder/tree/contrib/egg/egg-scrolled-window.c + """ + __gtype_name__ = "EggScrolledWindow" + + max_content_height = GObject.Property(type=int, default=-1, nick="Max Content Height", + blurb="The maximum height request that can be made") + max_content_width = GObject.Property(type=int, default=-1, nick="Max Content Width", + blurb="The maximum width request that can be made") + min_content_height = GObject.Property(type=int, default=-1, nick="Min Content Height", + blurb="The minimum height request that can be made") + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.connect_after("notify::max-content-height", lambda obj, param: self.queue_resize()) + self.connect_after("notify::max-content-width", lambda obj, param: self.queue_resize()) + + def set_min_content_height(self, value): + self.min_content_height = value + + def set_max_content_height(self, value): + self.max_content_height = value + + def set_max_content_width(self, value): + self.max_content_width = value + + def get_max_content_height(self): + return self.max_content_height + + def get_max_content_width(self): + return self.max_content_width + + def do_get_preferred_height(self): + min_height, natural_height = Gtk.ScrolledWindow.do_get_preferred_height(self) + child = self.get_child() + + if natural_height and self.max_content_height > -1 and child: + + style = self.get_style_context() + border = style.get_border(style.get_state()) + additional = border.top + border.bottom + + child_min_height, child_nat_height = child.get_preferred_height() + if child_nat_height > natural_height and self.max_content_height > natural_height: + natural_height = min(self.max_content_height, child_nat_height) + additional + elif natural_height > child_nat_height: + if child_nat_height < self.min_content_height: + return self.min_content_height, self.min_content_height + min_height, natural_height = child_min_height + additional, child_nat_height + additional + + return min_height, natural_height + + def do_get_preferred_width(self): + min_width, natural_width = Gtk.ScrolledWindow.do_get_preferred_width(self) + child = self.get_child() + + if natural_width and self.max_content_width > -1 and child: + + style = self.get_style_context() + border = style.get_border(style.get_state()) + additional = border.left + border.right + 1 + + child_min_width, child_nat_width = child.get_preferred_width() + if child_nat_width > natural_width and self.max_content_width > natural_width: + natural_width = min(self.max_content_width, child_nat_width) + additional + + return min_width, natural_width