[meld] diffgrid: Allow resizing file and folder comparison panes (bgo#576934)
- From: Kai Willadsen <kaiw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [meld] diffgrid: Allow resizing file and folder comparison panes (bgo#576934)
- Date: Sat, 7 Jun 2014 22:39:59 +0000 (UTC)
commit c9cd394c7f48deca8b4dfa70c4e0247f8e188ba4
Author: Marco Brito <bcaza null net>
Date: Wed Jun 4 15:12:42 2014 +0100
diffgrid: Allow resizing file and folder comparison panes (bgo#576934)
To individual adjust the width of each pane in a file or folder
comparison is something that can be pratical good to have. But by using
GtkGrid for layout its view creates a limitation of not allowing pane
resizing. Here is an implementation of a GtkGrid based custom widget,
by overriding its childrens size allocation and adding in the row at
the top of each LinkMap a drag handle, the size and position can easy
be set.
data/ui/dirdiff.ui | 54 +--------
data/ui/filediff.ui | 2 +-
meld/diffgrid.py | 312 +++++++++++++++++++++++++++++++++++++++++++++++
meld/ui/catalog.xml | 6 +
meld/ui/gladesupport.py | 1 +
5 files changed, 321 insertions(+), 54 deletions(-)
---
diff --git a/data/ui/dirdiff.ui b/data/ui/dirdiff.ui
index 4ee0f01..4478b01 100644
--- a/data/ui/dirdiff.ui
+++ b/data/ui/dirdiff.ui
@@ -102,7 +102,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkGrid" id="grid">
+ <object class="DiffGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
@@ -330,58 +330,6 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label6">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">2</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label7">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">4</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">6</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
<object class="GtkVBox" id="vbox0">
<property name="visible">True</property>
<property name="can_focus">False</property>
diff --git a/data/ui/filediff.ui b/data/ui/filediff.ui
index 4e43948..1fb7429 100644
--- a/data/ui/filediff.ui
+++ b/data/ui/filediff.ui
@@ -111,7 +111,7 @@
<signal name="key-press-event" handler="on_key_event" swapped="no"/>
<signal name="key-release-event" handler="on_key_event" swapped="no"/>
<child>
- <object class="GtkGrid" id="grid">
+ <object class="DiffGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">0</property>
diff --git a/meld/diffgrid.py b/meld/diffgrid.py
new file mode 100644
index 0000000..556d0dd
--- /dev/null
+++ b/meld/diffgrid.py
@@ -0,0 +1,312 @@
+# Copyright (C) 2014 Marco Brito <bcaza null net>
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+from gi.repository import Gtk
+from gi.repository import Gdk
+
+
+class DiffGrid(Gtk.Grid):
+ __gtype_name__ = "DiffGrid"
+
+ def __init__(self):
+ Gtk.Grid.__init__(self)
+ self._in_drag = False
+ self._drag_pos = -1
+ self._drag_handle = None
+ self._handle1 = HandleWindow()
+ self._handle2 = HandleWindow()
+
+ def do_realize(self):
+ Gtk.Grid.do_realize(self)
+ self._handle1.realize(self)
+ self._handle2.realize(self)
+
+ def do_unrealize(self):
+ self._handle1.unrealize()
+ self._handle2.unrealize()
+ Gtk.Grid.do_unrealize(self)
+
+ def do_map(self):
+ Gtk.Grid.do_map(self)
+ drag = self.get_child_at(2, 0)
+ if drag and drag.get_visible():
+ self._handle1.set_visible(True)
+
+ drag = self.get_child_at(4, 0)
+ if drag and drag.get_visible():
+ self._handle2.set_visible(True)
+
+ def do_unmap(self):
+ self._handle1.set_visible(False)
+ self._handle2.set_visible(False)
+ Gtk.Grid.do_unmap(self)
+
+ def _handle_set_prelight(self, window, flag):
+ if hasattr(window, "handle"):
+ window.handle.set_prelight(flag)
+ return True
+ return False
+
+ def do_enter_notify_event(self, event):
+ return self._handle_set_prelight(event.window, True)
+
+ def do_leave_notify_event(self, event):
+ if not self._in_drag:
+ return self._handle_set_prelight(event.window, False)
+ return False
+
+ def do_button_press_event(self, event):
+ if event.button & Gdk.BUTTON_PRIMARY:
+ self._drag_pos = event.x
+ self._in_drag = True
+ return True
+ return False
+
+ def do_button_release_event(self, event):
+ if event.button & Gdk.BUTTON_PRIMARY:
+ self._in_drag = False
+ return True
+ return False
+
+ def do_motion_notify_event(self, event):
+ if event.state & Gdk.ModifierType.BUTTON1_MASK:
+ if hasattr(event.window, "handle"):
+ x, y = event.window.get_position()
+ pos = round(x + event.x - self._drag_pos)
+ event.window.handle.set_position(pos)
+ self._drag_handle = event.window.handle
+ self.queue_resize_no_redraw()
+ return True
+ return False
+
+ def _calculate_positions(self, xmin, xmax, wlink1, wlink2,
+ wpane1, wpane2, wpane3):
+ wremain = max(0, xmax - xmin - wlink1 - wlink2)
+ pos1 = self._handle1.get_position(wremain, xmin)
+ pos2 = self._handle2.get_position(wremain, xmin + wlink1)
+
+ if not self._drag_handle:
+ npanes = 0
+ if wpane1 > 0:
+ npanes += 1
+ if wpane2 > 0:
+ npanes += 1
+ if wpane3 > 0:
+ npanes += 1
+ wpane = float(wremain) / max(1, npanes)
+ if wpane1 > 0:
+ wpane1 = wpane
+ if wpane2 > 0:
+ wpane2 = wpane
+ if wpane3 > 0:
+ wpane3 = wpane
+
+ xminlink1 = xmin + wpane1
+ xmaxlink2 = xmax - wpane3 - wlink2
+ wlinkpane = wlink1 + wpane2
+
+ if wpane1 == 0:
+ pos1 = xminlink1
+ if wpane3 == 0:
+ pos2 = xmaxlink2
+ if wpane2 == 0:
+ if wpane3 == 0:
+ pos1 = pos2 - wlink2
+ else:
+ pos2 = pos1 + wlink1
+
+ if self._drag_handle == self._handle2:
+ xminlink2 = xminlink1 + wlinkpane
+ pos2 = min(max(xminlink2, pos2), xmaxlink2)
+ xmaxlink1 = pos2 - wlinkpane
+ pos1 = min(max(xminlink1, pos1), xmaxlink1)
+ else:
+ xmaxlink1 = xmaxlink2 - wlinkpane
+ pos1 = min(max(xminlink1, pos1), xmaxlink1)
+ xminlink2 = pos1 + wlinkpane
+ pos2 = min(max(xminlink2, pos2), xmaxlink2)
+
+ self._handle1.set_position(pos1)
+ self._handle2.set_position(pos2)
+ return int(round(pos1)), int(round(pos2))
+
+ def do_size_allocate(self, allocation):
+ self.set_allocation(allocation)
+ wcols, hrows = self._get_min_sizes()
+ yrows = [allocation.y,
+ allocation.y + hrows[0],
+ allocation.y + allocation.height]
+
+ wmap1, wpane1, wlink1, wpane2, wlink2, wpane3, wmap2 = wcols
+ xmin = allocation.x + wmap1
+ xmax = allocation.x + allocation.width - wmap2
+ pos1, pos2 = self._calculate_positions(xmin, xmax,
+ wlink1, wlink2,
+ wpane1, wpane2, wpane3)
+ xmap1 = allocation.x
+ xpane1 = xmin
+ wpane1 = pos1 - xpane1
+ xlink1 = pos1
+ xpane2 = pos1 + wlink1
+ wpane2 = pos2 - xpane2
+ xlink2 = pos2
+ xpane3 = pos2 + wlink2
+ wpane3 = xmax - xpane3
+ xmap2 = xmax
+ self._set_column_allocation(0, xmap1, wmap1, yrows)
+ self._set_column_allocation(1, xpane1, wpane1, yrows)
+ self._set_column_allocation(2, xlink1, wlink1, yrows)
+ self._set_column_allocation(3, xpane2, wpane2, yrows)
+ self._set_column_allocation(4, xlink2, wlink2, yrows)
+ self._set_column_allocation(5, xpane3, wpane3, yrows)
+ self._set_column_allocation(6, xmap2, wmap2, yrows)
+
+ if self.get_realized():
+ mapped = self.get_mapped()
+ ydrag = yrows[0]
+ hdrag = yrows[1] - yrows[0]
+ self._handle1.set_visible(mapped and wlink1 > 0)
+ self._handle1.move_resize(xlink1, ydrag, wlink1, hdrag)
+ self._handle2.set_visible(mapped and wlink2 > 0)
+ self._handle2.move_resize(xlink2, ydrag, wlink2, hdrag)
+
+ def _set_column_allocation(self, col, x, width, rows):
+ for row in range(0, 2):
+ child = self.get_child_at(col, row)
+ if child and child.get_visible():
+ alloc = self.get_allocation()
+ alloc.x = x
+ alloc.y = rows[row]
+ alloc.width = width
+ alloc.height = rows[row+1] - alloc.y
+ child.size_allocate(alloc)
+
+ def _get_min_sizes(self):
+ hrows = [0] * 2
+ wcols = [0] * 7
+ for row in range(0, 2):
+ for col in range(0, 7):
+ child = self.get_child_at(col, row)
+ if child and child.get_visible():
+ msize, nsize = child.get_preferred_size()
+ wcols[col] = max(wcols[col], msize.width, nsize.width)
+ hrows[row] = max(hrows[row], msize.height, nsize.height)
+ return wcols, hrows
+
+ def do_draw(self, context):
+ Gtk.Grid.do_draw(self, context)
+ self._handle1.draw(context)
+ self._handle2.draw(context)
+
+
+class HandleWindow():
+ def __init__(self):
+ self._widget = None
+ self._window = None
+ self._area_x = -1
+ self._area_y = -1
+ self._area_width = 1
+ self._area_height = 1
+ self._prelit = False
+ self._pos = 0.0
+ self._transform = (0, 0)
+
+ def get_position(self, width, xtrans):
+ self._transform = (width, xtrans)
+ return float(self._pos * width) + xtrans
+
+ def set_position(self, pos):
+ width, xtrans = self._transform
+ self._pos = float(pos - xtrans) / width
+
+ def realize(self, widget):
+ attr = Gdk.WindowAttr()
+ attr.window_type = Gdk.WindowType.CHILD
+ attr.x = self._area_x
+ attr.y = self._area_y
+ attr.width = self._area_width
+ attr.height = self._area_height
+ attr.wclass = Gdk.WindowWindowClass.INPUT_OUTPUT
+ attr.event_mask = (widget.get_events() |
+ Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK)
+ attr.cursor = Gdk.Cursor.new_for_display(widget.get_display(),
+ Gdk.CursorType.
+ SB_H_DOUBLE_ARROW)
+ attr_mask = (Gdk.WindowAttributesType.X |
+ Gdk.WindowAttributesType.Y |
+ Gdk.WindowAttributesType.CURSOR)
+
+ parent = widget.get_parent_window()
+ self._window = Gdk.Window(parent, attr, attr_mask)
+ self._window.handle = self
+ self._widget = widget
+ self._widget.register_window(self._window)
+
+ def unrealize(self):
+ self._widget.unregister_window(self._window)
+
+ def set_visible(self, visible):
+ if visible:
+ self._window.show()
+ else:
+ self._window.hide()
+
+ def move_resize(self, x, y, width, height):
+ self._window.move_resize(x, y, width, height)
+ self._area_x = x
+ self._area_y = y
+ self._area_width = width
+ self._area_height = height
+
+ def set_prelight(self, flag):
+ self._prelit = flag
+ self._widget.queue_draw_area(self._area_x, self._area_y,
+ self._area_width, self._area_height)
+
+ def draw(self, cairocontext):
+ alloc = self._widget.get_allocation()
+ padding = 5
+ x = self._area_x - alloc.x + padding
+ y = self._area_y - alloc.y + padding
+ width = max(0, self._area_width - 2 * padding)
+ height = max(0, self._area_height - 2 * padding)
+
+ if width == 0 or height == 0:
+ return
+
+ stylecontext = self._widget.get_style_context()
+ state = self._widget.get_state_flags()
+ if self._widget.is_focus():
+ state |= Gtk.StateFlags.SELECTED
+ if self._prelit:
+ state |= Gtk.StateFlags.PRELIGHT
+
+ if Gtk.cairo_should_draw_window(cairocontext, self._window):
+ stylecontext.save()
+ stylecontext.set_state(state)
+ stylecontext.add_class(Gtk.STYLE_CLASS_PANE_SEPARATOR)
+ color = stylecontext.get_background_color(state)
+ if color.alpha > 0.0:
+ Gtk.render_handle(stylecontext, cairocontext,
+ x, y, width, height)
+ else:
+ xcenter = x + width / 2.0
+ Gtk.render_line(stylecontext, cairocontext,
+ xcenter, y, xcenter, y + height)
+ stylecontext.restore()
diff --git a/meld/ui/catalog.xml b/meld/ui/catalog.xml
index ddfdf83..0bc2954 100644
--- a/meld/ui/catalog.xml
+++ b/meld/ui/catalog.xml
@@ -3,6 +3,12 @@
<init-function>glade_python_init</init-function>
<glade-widget-classes>
+ <glade-widget-class title="DiffGrid" name="DiffGrid" generic-name="diffgrid">
+ <properties>
+ <property id="n-rows" default="2" query="False"/>
+ <property id="n-columns" default="7" query="False"/>
+ </properties>
+ </glade-widget-class>
<glade-widget-class title="DiffMap" name="DiffMap" generic-name="diffmap"/>
<glade-widget-class title="LinkMap" name="LinkMap" generic-name="linkmap"/>
<glade-widget-class title="MeldSourceView" name="MeldSourceView" generic-name="meldsourceview"/>
diff --git a/meld/ui/gladesupport.py b/meld/ui/gladesupport.py
index 2827db8..ce77eae 100644
--- a/meld/ui/gladesupport.py
+++ b/meld/ui/gladesupport.py
@@ -1,4 +1,5 @@
+from meld import diffgrid
from meld import diffmap
from meld import linkmap
from meld import preferences
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]