Extract ScrolledWindow in the widgets module
This commit is contained in:
parent
8b1b7cbe7e
commit
bdac88460f
110
src/main.py
110
src/main.py
|
@ -46,6 +46,7 @@ from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, Searc
|
|||
from diffuse.preferences import Preferences
|
||||
from diffuse.resources import Resources
|
||||
from diffuse.vcs.vcs_registry import VcsRegistry
|
||||
from diffuse.widgets import ScrolledWindow
|
||||
|
||||
# avoid some dictionary lookups when string.whitespace is used in loops
|
||||
# this is sorted based upon frequency to speed up code for stripping whitespace
|
||||
|
@ -178,111 +179,6 @@ def convert_to_format(s, format):
|
|||
s += '\r'
|
||||
return s
|
||||
|
||||
# utility method to step advance an adjustment
|
||||
def step_adjustment(adj, delta):
|
||||
v = adj.get_value() + delta
|
||||
# clamp to the allowed range
|
||||
v = max(v, int(adj.get_lower()))
|
||||
v = min(v, int(adj.get_upper() - adj.get_page_size()))
|
||||
adj.set_value(v)
|
||||
|
||||
# This is a replacement for Gtk.ScrolledWindow as it forced expose events to be
|
||||
# handled immediately after changing the viewport position. This could cause
|
||||
# the application to become unresponsive for a while as it processed a large
|
||||
# queue of keypress and expose event pairs.
|
||||
class ScrolledWindow(Gtk.Grid):
|
||||
scroll_directions = set((Gdk.ScrollDirection.UP,
|
||||
Gdk.ScrollDirection.DOWN,
|
||||
Gdk.ScrollDirection.LEFT,
|
||||
Gdk.ScrollDirection.RIGHT))
|
||||
|
||||
def __init__(self, hadj, vadj):
|
||||
Gtk.Grid.__init__(self)
|
||||
self.position = (0, 0)
|
||||
self.scroll_count = 0
|
||||
self.partial_redraw = False
|
||||
|
||||
self.hadj, self.vadj = hadj, vadj
|
||||
vport = Gtk.Viewport.new()
|
||||
darea = Gtk.DrawingArea.new()
|
||||
darea.add_events(Gdk.EventMask.SCROLL_MASK)
|
||||
self.darea = darea
|
||||
# replace darea's queue_draw_area with our own so we can tell when
|
||||
# to disable/enable our scrolling optimisation
|
||||
self.darea_queue_draw_area = darea.queue_draw_area
|
||||
darea.queue_draw_area = self.redraw_region
|
||||
vport.add(darea)
|
||||
darea.show()
|
||||
self.attach(vport, 0, 0, 1, 1)
|
||||
vport.set_vexpand(True)
|
||||
vport.set_hexpand(True)
|
||||
vport.show()
|
||||
|
||||
self.vbar = bar = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, vadj)
|
||||
self.attach(bar, 1, 0, 1, 1)
|
||||
bar.show()
|
||||
|
||||
self.hbar = bar = Gtk.Scrollbar.new(Gtk.Orientation.HORIZONTAL, hadj)
|
||||
self.attach(bar, 0, 1, 1, 1)
|
||||
bar.show()
|
||||
|
||||
# listen to our signals
|
||||
hadj.connect('value-changed', self.value_changed_cb)
|
||||
vadj.connect('value-changed', self.value_changed_cb)
|
||||
darea.connect('configure-event', self.configure_cb)
|
||||
darea.connect('scroll-event', self.scroll_cb)
|
||||
darea.connect('draw', self.draw_cb)
|
||||
|
||||
# updates the adjustments to match the new widget size
|
||||
def configure_cb(self, widget, event):
|
||||
w, h = event.width, event.height
|
||||
for adj, d in (self.hadj, w), (self.vadj, h):
|
||||
v = adj.get_value()
|
||||
if v + d > adj.get_upper():
|
||||
adj.set_value(max(0, adj.get_upper() - d))
|
||||
adj.set_page_size(d)
|
||||
adj.set_page_increment(d)
|
||||
|
||||
# update the vertical adjustment when the mouse's scroll wheel is used
|
||||
def scroll_cb(self, widget, event):
|
||||
d = event.direction
|
||||
if d in self.scroll_directions:
|
||||
delta = 100
|
||||
if d in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.LEFT):
|
||||
delta = -delta
|
||||
vertical = (d in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.DOWN))
|
||||
if event.state & Gdk.ModifierType.SHIFT_MASK:
|
||||
vertical = not vertical
|
||||
if vertical:
|
||||
adj = self.vadj
|
||||
else:
|
||||
adj = self.hadj
|
||||
step_adjustment(adj, delta)
|
||||
|
||||
def value_changed_cb(self, widget):
|
||||
old_x, old_y = self.position
|
||||
pos_x = int(self.hadj.get_value())
|
||||
pos_y = int(self.vadj.get_value())
|
||||
self.position = (pos_x, pos_y)
|
||||
if self.darea.get_window() is not None:
|
||||
# window.scroll() although visually nice, is slow, revert to
|
||||
# queue_draw() if scroll a lot without seeing an expose event
|
||||
if self.scroll_count < 2 and not self.partial_redraw:
|
||||
self.scroll_count += 1
|
||||
self.darea.get_window().scroll(old_x - pos_x, old_y - pos_y)
|
||||
else:
|
||||
self.partial_redraw = False
|
||||
self.darea.queue_draw()
|
||||
|
||||
def draw_cb(self, widget, cr):
|
||||
self.scroll_count = 0
|
||||
|
||||
# replacement for darea.queue_draw_area that notifies us when a partial
|
||||
# redraw happened
|
||||
def redraw_region(self, x, y, w, h):
|
||||
self.partial_redraw = True
|
||||
self.darea_queue_draw_area(x, y, w, h)
|
||||
|
||||
# Enforcing manual alignment is accomplished by dividing the lines of text into
|
||||
# sections that are matched independently. 'blocks' is an array of integers
|
||||
# describing how many lines (including null lines for spacing) that are in each
|
||||
|
@ -2770,9 +2666,9 @@ class FileDiffViewer(Gtk.Grid):
|
|||
def diffmap_scroll_cb(self, widget, event):
|
||||
delta = 100
|
||||
if event.direction == Gdk.ScrollDirection.UP:
|
||||
step_adjustment(self.vadj, -delta)
|
||||
utils.step_adjustment(self.vadj, -delta)
|
||||
elif event.direction == Gdk.ScrollDirection.DOWN:
|
||||
step_adjustment(self.vadj, delta)
|
||||
utils.step_adjustment(self.vadj, delta)
|
||||
|
||||
# redraws the overview map when a portion is exposed
|
||||
def diffmap_draw_cb(self, widget, cr):
|
||||
|
|
|
@ -37,6 +37,7 @@ diffuse_sources = [
|
|||
'preferences.py',
|
||||
'resources.py',
|
||||
'utils.py',
|
||||
'widgets.py',
|
||||
]
|
||||
|
||||
install_data(diffuse_sources, install_dir: moduledir)
|
||||
|
|
14
src/utils.py
14
src/utils.py
|
@ -264,6 +264,14 @@ def null_to_empty(s):
|
|||
s = ''
|
||||
return s
|
||||
|
||||
# utility method to step advance an adjustment
|
||||
def step_adjustment(adj, delta):
|
||||
v = adj.get_value() + delta
|
||||
# clamp to the allowed range
|
||||
v = max(v, int(adj.get_lower()))
|
||||
v = min(v, int(adj.get_upper() - adj.get_page_size()))
|
||||
adj.set_value(v)
|
||||
|
||||
|
||||
# use the program's location as a starting place to search for supporting files
|
||||
# such as icon and help documentation
|
||||
|
@ -279,9 +287,9 @@ lang = locale.getdefaultlocale()[0]
|
|||
if isWindows():
|
||||
# gettext looks for the language using environment variables which
|
||||
# are normally not set on Windows so we try setting it for them
|
||||
for v in 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE':
|
||||
if v in os.environ:
|
||||
lang = os.environ[v]
|
||||
for lang_env in 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE':
|
||||
if lang_env in os.environ:
|
||||
lang = os.environ[lang_env]
|
||||
# remove any additional languages, encodings, or modifications
|
||||
for c in ':.@':
|
||||
lang = lang.split(c)[0]
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
# Diffuse: a graphical tool for merging and comparing text files.
|
||||
#
|
||||
# Copyright (C) 2019 Derrick Moser <derrick_moser@yahoo.com>
|
||||
# Copyright (C) 2021 Romain Failliot <romain.failliot@foolstep.com>
|
||||
#
|
||||
# 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 2 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 warranty of
|
||||
# MERCHANTABILITY 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, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, Gdk
|
||||
# pylint: enable=wrong-import-position
|
||||
|
||||
from diffuse import utils
|
||||
|
||||
# This is a replacement for Gtk.ScrolledWindow as it forced expose events to be
|
||||
# handled immediately after changing the viewport position. This could cause
|
||||
# the application to become unresponsive for a while as it processed a large
|
||||
# queue of keypress and expose event pairs.
|
||||
class ScrolledWindow(Gtk.Grid):
|
||||
scroll_directions = set((Gdk.ScrollDirection.UP,
|
||||
Gdk.ScrollDirection.DOWN,
|
||||
Gdk.ScrollDirection.LEFT,
|
||||
Gdk.ScrollDirection.RIGHT))
|
||||
|
||||
def __init__(self, hadj, vadj):
|
||||
Gtk.Grid.__init__(self)
|
||||
self.position = (0, 0)
|
||||
self.scroll_count = 0
|
||||
self.partial_redraw = False
|
||||
|
||||
self.hadj, self.vadj = hadj, vadj
|
||||
vport = Gtk.Viewport.new()
|
||||
darea = Gtk.DrawingArea.new()
|
||||
darea.add_events(Gdk.EventMask.SCROLL_MASK)
|
||||
self.darea = darea
|
||||
# replace darea's queue_draw_area with our own so we can tell when
|
||||
# to disable/enable our scrolling optimisation
|
||||
self.darea_queue_draw_area = darea.queue_draw_area
|
||||
darea.queue_draw_area = self.redraw_region
|
||||
vport.add(darea)
|
||||
darea.show()
|
||||
self.attach(vport, 0, 0, 1, 1)
|
||||
vport.set_vexpand(True)
|
||||
vport.set_hexpand(True)
|
||||
vport.show()
|
||||
|
||||
self.vbar = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, vadj)
|
||||
self.attach(self.vbar, 1, 0, 1, 1)
|
||||
self.vbar.show()
|
||||
|
||||
self.hbar = Gtk.Scrollbar.new(Gtk.Orientation.HORIZONTAL, hadj)
|
||||
self.attach(self.hbar, 0, 1, 1, 1)
|
||||
self.hbar.show()
|
||||
|
||||
# listen to our signals
|
||||
hadj.connect('value-changed', self.value_changed_cb)
|
||||
vadj.connect('value-changed', self.value_changed_cb)
|
||||
darea.connect('configure-event', self.configure_cb)
|
||||
darea.connect('scroll-event', self.scroll_cb)
|
||||
darea.connect('draw', self.draw_cb)
|
||||
|
||||
# updates the adjustments to match the new widget size
|
||||
def configure_cb(self, widget, event):
|
||||
w, h = event.width, event.height
|
||||
for adj, d in (self.hadj, w), (self.vadj, h):
|
||||
v = adj.get_value()
|
||||
if v + d > adj.get_upper():
|
||||
adj.set_value(max(0, adj.get_upper() - d))
|
||||
adj.set_page_size(d)
|
||||
adj.set_page_increment(d)
|
||||
|
||||
# update the vertical adjustment when the mouse's scroll wheel is used
|
||||
def scroll_cb(self, widget, event):
|
||||
d = event.direction
|
||||
if d in self.scroll_directions:
|
||||
delta = 100
|
||||
if d in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.LEFT):
|
||||
delta = -delta
|
||||
vertical = (d in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.DOWN))
|
||||
if event.state & Gdk.ModifierType.SHIFT_MASK:
|
||||
vertical = not vertical
|
||||
if vertical:
|
||||
adj = self.vadj
|
||||
else:
|
||||
adj = self.hadj
|
||||
utils.step_adjustment(adj, delta)
|
||||
|
||||
def value_changed_cb(self, widget):
|
||||
old_x, old_y = self.position
|
||||
pos_x = int(self.hadj.get_value())
|
||||
pos_y = int(self.vadj.get_value())
|
||||
self.position = (pos_x, pos_y)
|
||||
if self.darea.get_window() is not None:
|
||||
# window.scroll() although visually nice, is slow, revert to
|
||||
# queue_draw() if scroll a lot without seeing an expose event
|
||||
if self.scroll_count < 2 and not self.partial_redraw:
|
||||
self.scroll_count += 1
|
||||
self.darea.get_window().scroll(old_x - pos_x, old_y - pos_y)
|
||||
else:
|
||||
self.partial_redraw = False
|
||||
self.darea.queue_draw()
|
||||
|
||||
def draw_cb(self, widget, cr):
|
||||
self.scroll_count = 0
|
||||
|
||||
# replacement for darea.queue_draw_area that notifies us when a partial
|
||||
# redraw happened
|
||||
def redraw_region(self, x, y, w, h):
|
||||
self.partial_redraw = True
|
||||
self.darea_queue_draw_area(x, y, w, h)
|
Loading…
Reference in New Issue