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
This commit is contained in:
Philipp Hörist 2017-07-18 00:53:50 +02:00
parent c963fc5dcc
commit b8aaf09c13
4 changed files with 116 additions and 76 deletions

View File

@ -629,21 +629,7 @@
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
<placeholder/>
</child>
</object>
<packing>

View File

@ -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.

View File

@ -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'] = '<span style="text-decoration: line-through;">'
self.end_tags['strike'] = '</span>'
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()

89
gajim/scrolled_window.py Normal file
View File

@ -0,0 +1,89 @@
# -*- coding:utf-8 -*-
# Copyright (C) 2015 Patrick Griffis <tingping@tingping.se>
# Copyright (C) 2014 Christian Hergert <christian@hergert.me>
#
# 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 <http://www.gnu.org/licenses/>.
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