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:
parent
c963fc5dcc
commit
b8aaf09c13
|
@ -629,21 +629,7 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow" id="message_scrolledwindow">
|
<placeholder/>
|
||||||
<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>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
|
@ -44,6 +44,7 @@ import notify
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import emoticons
|
import emoticons
|
||||||
|
from scrolled_window import ScrolledWindow
|
||||||
from common import events
|
from common import events
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import helpers
|
from common import helpers
|
||||||
|
@ -327,15 +328,21 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
self.last_received_id = {} # one per name
|
self.last_received_id = {} # one per name
|
||||||
|
|
||||||
# add MessageTextView to UI and connect signals
|
# add MessageTextView to UI and connect signals
|
||||||
self.msg_scrolledwindow = self.xml.get_object('message_scrolledwindow')
|
|
||||||
self.msg_textview = MessageTextView()
|
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)
|
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',
|
id_ = self.msg_textview.connect('key_press_event',
|
||||||
self._on_message_textview_key_press_event)
|
self._on_message_textview_key_press_event)
|
||||||
self.handlers[id_] = self.msg_textview
|
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',
|
id_ = self.msg_textview.connect('populate_popup',
|
||||||
self.on_msg_textview_populate_popup)
|
self.on_msg_textview_populate_popup)
|
||||||
self.handlers[id_] = self.msg_textview
|
self.handlers[id_] = self.msg_textview
|
||||||
|
@ -369,8 +376,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
# For XEP-0172
|
# For XEP-0172
|
||||||
self.user_nick = None
|
self.user_nick = None
|
||||||
|
|
||||||
self.smooth = True
|
|
||||||
|
|
||||||
self.command_hits = []
|
self.command_hits = []
|
||||||
self.last_key_tabs = False
|
self.last_key_tabs = False
|
||||||
|
|
||||||
|
@ -693,6 +698,10 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
self.send_message(message, xhtml=xhtml)
|
self.send_message(message, xhtml=xhtml)
|
||||||
else:
|
else:
|
||||||
message_buffer.insert_at_cursor('\n')
|
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
|
return True
|
||||||
elif event.keyval == Gdk.KEY_z: # CTRL+z
|
elif event.keyval == Gdk.KEY_z: # CTRL+z
|
||||||
|
@ -1157,60 +1166,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
self.conv_textview.scroll_to_end_iter()
|
self.conv_textview.scroll_to_end_iter()
|
||||||
return False
|
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):
|
def on_conversation_vadjustment_changed(self, adjustment):
|
||||||
# used to stay at the end of the textview when we shrink conversation
|
# used to stay at the end of the textview when we shrink conversation
|
||||||
# textview.
|
# textview.
|
||||||
|
|
|
@ -29,6 +29,7 @@ from gi.repository import GLib
|
||||||
from gi.repository import Pango
|
from gi.repository import Pango
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
|
import gtkgui_helpers
|
||||||
|
|
||||||
class MessageTextView(Gtk.TextView):
|
class MessageTextView(Gtk.TextView):
|
||||||
"""
|
"""
|
||||||
|
@ -38,7 +39,7 @@ class MessageTextView(Gtk.TextView):
|
||||||
UNDO_LIMIT = 20
|
UNDO_LIMIT = 20
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
GObject.GObject.__init__(self)
|
Gtk.TextView.__init__(self)
|
||||||
|
|
||||||
# set properties
|
# set properties
|
||||||
self.set_border_width(1)
|
self.set_border_width(1)
|
||||||
|
@ -79,6 +80,15 @@ class MessageTextView(Gtk.TextView):
|
||||||
self.begin_tags['strike'] = '<span style="text-decoration: line-through;">'
|
self.begin_tags['strike'] = '<span style="text-decoration: line-through;">'
|
||||||
self.end_tags['strike'] = '</span>'
|
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):
|
def make_clickable_urls(self, text):
|
||||||
_buffer = self.get_buffer()
|
_buffer = self.get_buffer()
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue