This site hosts my projects.
changeset: 8007:c13d8a7fe816
tag: tip
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sat Jul 12 12:31:22 2025 +0200
summary: Milestones do not affect position of point events.
diff -r 7ab76958ed12 -r c13d8a7fe816 documentation/changelog.rst
--- a/documentation/changelog.rst Sat Jul 12 12:09:31 2025 +0200
+++ b/documentation/changelog.rst Sat Jul 12 12:31:22 2025 +0200
@@ -41,6 +41,8 @@
* When milestones overlap, the one you visually click is the one that becomes
selected. Previously the "first match" became selected.
+* Milestones do not affect position of point events.
+
Windows installer:
* The Windows installer is now built on Linux/Wine and only for 64 bit platforms.
diff -r 7ab76958ed12 -r c13d8a7fe816 source/timelinelib/canvas/drawing/scene.py
--- a/source/timelinelib/canvas/drawing/scene.py Sat Jul 12 12:09:31 2025 +0200
+++ b/source/timelinelib/canvas/drawing/scene.py Sat Jul 12 12:31:22 2025 +0200
@@ -478,7 +478,8 @@
def _get_list_with_overlapping_point_events(self, event_rect):
return [(event, rect) for (event, rect) in self.event_data
if (self._rects_overlap(event_rect, rect) and
- rect.Y < self.divider_y)]
+ rect.Y < self.divider_y) and
+ not rect.is_allowed_to_overlap()]
def _rects_overlap(self, rect1, rect2):
REMOVE_X_PADDING = 2 + self._outer_padding * 2
changeset: 8006:7ab76958ed12
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sat Jul 12 12:09:31 2025 +0200
summary: Select the milestone that was visually clicked instead of the first matching one.
diff -r 25c17562e3f3 -r 7ab76958ed12 documentation/changelog.rst
--- a/documentation/changelog.rst Fri Jul 11 18:11:51 2025 +0200
+++ b/documentation/changelog.rst Sat Jul 12 12:09:31 2025 +0200
@@ -38,6 +38,9 @@
divider line if preference "Never show period Events as point Events" is
checked.
+* When milestones overlap, the one you visually click is the one that becomes
+ selected. Previously the "first match" became selected.
+
Windows installer:
* The Windows installer is now built on Linux/Wine and only for 64 bit platforms.
diff -r 25c17562e3f3 -r 7ab76958ed12 source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 18:11:51 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Sat Jul 12 12:09:31 2025 +0200
@@ -231,19 +231,13 @@
return self.snap(start), self.snap(end)
def event_at(self, x, y, alt_down=False):
- container_event = None
- for (event, rect) in self.scene.event_data:
+ for (event, rect) in reversed(self.scene.event_data):
if event.is_container():
rect = self._adjust_container_rect_for_hittest(rect)
if rect.Contains(wx.Point(x, y)):
self.set_horizontal_mouse_position_factor(event, x)
- if event.is_container():
- if self.scene.view_properties.is_selected(event):
- return event
- container_event = event
- else:
- return event
- return container_event
+ return event
+ return None
def set_horizontal_mouse_position_factor(self, event, x):
try:
changeset: 8005:25c17562e3f3
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 18:11:51 2025 +0200
summary: Prevent "SyntaxWarning: invalid escape sequence '\ '".
diff -r 43e78c60f59a -r 25c17562e3f3 source/timelinelib/canvas/drawing/drawers/ballondrawer.py
--- a/source/timelinelib/canvas/drawing/drawers/ballondrawer.py Fri Jul 11 12:38:41 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/ballondrawer.py Fri Jul 11 18:11:51 2025 +0200
@@ -115,11 +115,11 @@
W
|----------------|
______________ _
- / \ | R = Corner Radius
+ * * | R = Corner Radius
| | | AA = Left Arrow-leg angle
| W_ARROW | | H MARGIN = Text margin
| |--| | | * = Starting point
- \____ ______/ _
+ *____ ______* _
/ / |
/_/ | H_ARROW
* -
changeset: 8004:43e78c60f59a
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 12:38:41 2025 +0200
summary: Get rid of symbol_rect by introducing FloatingRect.
diff -r c23d402b991b -r 43e78c60f59a source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 12:15:03 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 12:38:41 2025 +0200
@@ -243,10 +243,6 @@
container_event = event
else:
return event
- else:
- if hasattr(event, 'symbol_rect'):
- if event.symbol_rect.Contains(wx.Point(x, y)):
- return event
return container_event
def set_horizontal_mouse_position_factor(self, event, x):
@@ -548,8 +544,6 @@
def _draw_ballon(self, event, event_rect, sticky):
"""Draw one ballon on a selected event that has 'description' data."""
ballon_drawer = BallonDrawer(self.dc, self.scene, self.appearance, event)
- if hasattr(event, 'symbol_rect'):
- event_rect = event.symbol_rect
self.balloon_data.append(ballon_drawer.draw(event_rect, sticky))
def get_period_xpos(self, time_period):
diff -r c23d402b991b -r 43e78c60f59a source/timelinelib/canvas/drawing/rect.py
--- a/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 12:15:03 2025 +0200
+++ b/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 12:38:41 2025 +0200
@@ -32,4 +32,13 @@
return clone
def Clone(self):
- return Rect(self.X, self.Y, self.Width, self.Height)
+ return self.__class__(self.X, self.Y, self.Width, self.Height)
+
+ def is_allowed_to_overlap(self):
+ return False
+
+
+class FloatingRect(Rect):
+
+ def is_allowed_to_overlap(self):
+ return True
diff -r c23d402b991b -r 43e78c60f59a source/timelinelib/canvas/drawing/scene.py
--- a/source/timelinelib/canvas/drawing/scene.py Fri Jul 11 12:15:03 2025 +0200
+++ b/source/timelinelib/canvas/drawing/scene.py Fri Jul 11 12:38:41 2025 +0200
@@ -22,6 +22,7 @@
from timelinelib.canvas.data import TimePeriod
from timelinelib.features.experimental.experimentalfeatures import EXTENDED_CONTAINER_STRATEGY
from timelinelib.canvas.drawing.rect import Rect
+from timelinelib.canvas.drawing.rect import FloatingRect
FORWARD = 1
@@ -302,7 +303,10 @@
def _calc_ideal_rect_for_non_period_event(self, event):
if self.never_show_period_events_as_point_events() and event.is_period():
- return self._calc_invisible_wx_rect()
+ x = self.x_pos_for_time(event.mean_time())
+ y0 = self.divider_y
+ y1 = y0 + 10
+ return FloatingRect(x - 5, y1 - 5, 10, 10).CloneInflate(self._outer_padding, self._outer_padding)
rw = self._calc_width_for_non_period_event(event)
rh = self._calc_height_for_non_period_event(event)
rx = self._calc_x_pos_for_non_period_event(event, rw)
@@ -311,12 +315,9 @@
rect = Rect(rx, ry, rw, rh)
rect.SetWidth(rect.GetHeight())
rect.SetX(self._metrics.calc_x(event.get_time_period().start_time) - rw // 2)
- return rect
+ return FloatingRect(rect)
return self._calc_ideal_wx_rect(rx, ry, rw, rh)
- def _calc_invisible_wx_rect(self):
- return self._calc_ideal_wx_rect(-1, -1, 0, 0)
-
def _calc_width_for_non_period_event(self, event):
tw, th = self._get_text_size(event.get_text())
rw = tw + 2 * self._inner_padding + 2 * self._outer_padding
@@ -401,7 +402,7 @@
return len(self.events_from_db) - visible_events_count
def _prevent_overlapping_by_adjusting_rect_y(self, event, event_rect):
- if event.is_milestone():
+ if event_rect.is_allowed_to_overlap():
return
if self._is_subevent_in_extended_container_strategy(event):
self._adjust_subevent_rect(event, event_rect)
diff -r c23d402b991b -r 43e78c60f59a source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 12:15:03 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 12:38:41 2025 +0200
@@ -46,21 +46,20 @@
self.center_text = scene.center_text()
if event.is_milestone():
DefaultMilestoneDrawer(rect, event, selected, view_properties).draw(dc)
- elif scene.never_show_period_events_as_point_events() and rect.y < scene.divider_y and event.is_period():
- self._draw_period_event_as_symbol_below_divider_line(dc, scene, event)
+ elif scene.never_show_period_events_as_point_events() and rect.is_allowed_to_overlap() and event.is_period():
+ self._draw_period_event_as_symbol_below_divider_line(dc, rect, scene, event)
else:
self._draw_event_box(dc, rect, event, selected)
- def _draw_period_event_as_symbol_below_divider_line(self, dc, scene, event):
+ def _draw_period_event_as_symbol_below_divider_line(self, dc, rect, scene, event):
dc.DestroyClippingRegion()
- x = scene.x_pos_for_time(event.mean_time())
+ x = rect.X + rect.Width // 2
y0 = scene.divider_y
- y1 = y0 + 10
+ y1 = rect.Y + rect.Height // 2
dc.SetBrush(black_solid_brush())
dc.SetPen(black_solid_pen(1))
dc.DrawLine(x, y0, x, y1)
dc.DrawCircle(x, y1, 2)
- event.symbol_rect = Rect(x - 5, y1 - 5, 10, 10)
def _draw_event_box(self, dc, rect, event, selected):
self._draw_background(dc, rect, event)
changeset: 8001:c9f68492c01b
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 11:51:21 2025 +0200
summary: Cursor.rect returns a Rect object.
diff -r 546241b2cec8 -r c9f68492c01b source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:37:43 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:51:21 2025 +0200
@@ -188,7 +188,7 @@
if view_properties._selection_rect:
self.dc.SetPen(wx.BLACK_PEN)
self.dc.SetBrush(wx.Brush(wx.WHITE, style=BRUSHSTYLE_TRANSPARENT))
- self.dc.DrawRectangle(*view_properties._selection_rect)
+ self.dc.DrawRectangle(view_properties._selection_rect)
def _perform_normal_drawing(self, view_properties):
self._draw_period_selection(view_properties)
diff -r 546241b2cec8 -r c9f68492c01b source/timelinelib/wxgui/cursor.py
--- a/source/timelinelib/wxgui/cursor.py Fri Jul 11 11:37:43 2025 +0200
+++ b/source/timelinelib/wxgui/cursor.py Fri Jul 11 11:51:21 2025 +0200
@@ -21,6 +21,9 @@
"""
+from timelinelib.canvas.drawing.rect import Rect
+
+
class Cursor:
def __init__(self, x=0, y=0):
@@ -54,7 +57,7 @@
def rect(self):
x0, y0 = self._start_pos
x1, y1 = self._current_pos
- return min(x0, x1), min(y0, y1), abs(x1 - x0), abs(y0 - y1)
+ return Rect(min(x0, x1), min(y0, y1), abs(x1 - x0), abs(y0 - y1))
def has_moved(self):
return self._has_moved
diff -r 546241b2cec8 -r c9f68492c01b test/specs/wxgui/cursor.py
--- a/test/specs/wxgui/cursor.py Fri Jul 11 11:37:43 2025 +0200
+++ b/test/specs/wxgui/cursor.py Fri Jul 11 11:51:21 2025 +0200
@@ -18,6 +18,7 @@
from timelinelib.wxgui.cursor import Cursor
from timelinelib.test.cases.unit import UnitTestCase
+from timelinelib.canvas.drawing.rect import Rect
X = 3
@@ -63,7 +64,7 @@
def test_can_calculate_rect(self):
self.cursor.move(XX, YY)
- self.assertEqual((XX, YY, X - XX, Y - YY), self.cursor.rect)
+ self.assertEqual(Rect(XX, YY, X - XX, Y - YY), self.cursor.rect)
def setUp(self):
self.cursor = Cursor(X, Y)
changeset: 8003:c23d402b991b
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 12:15:03 2025 +0200
summary: Ensure Rect is only instantiated with (x, y, width, height).
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/drawing/graphobject.py
--- a/source/timelinelib/canvas/drawing/graphobject.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/drawing/graphobject.py Fri Jul 11 12:15:03 2025 +0200
@@ -83,7 +83,7 @@
@property
def rect(self):
"""Getter property."""
- return Rect(self._rect)
+ return Rect(*self._rect)
@property
def width(self):
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/drawing/rect.py
--- a/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 12:15:03 2025 +0200
@@ -22,6 +22,14 @@
class Rect(wx.Rect):
def CloneInflate(self, dx, dy):
- clone = Rect(self)
+ clone = self.Clone()
clone.Inflate(dx, dy)
return clone
+
+ def CloneDeflate(self, dx, dy):
+ clone = self.Clone()
+ clone.Deflate(dx, dy)
+ return clone
+
+ def Clone(self):
+ return Rect(self.X, self.Y, self.Width, self.Height)
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 12:15:03 2025 +0200
@@ -246,8 +246,7 @@
def _draw_handles(self, dc, event, rect):
def draw_frame_around_event():
- small_rect = Rect(*rect)
- small_rect.Deflate(1, 1)
+ small_rect = rect.CloneDeflate(1, 1)
border_color = event.get_border_color()
border_color = darken_color(border_color)
pen = wx.Pen(border_color, 1, wx.PENSTYLE_SOLID)
@@ -276,10 +275,8 @@
@staticmethod
def _inflate_clipping_region(dc, rect):
- copy = Rect(*rect)
- copy.Inflate(10, 0)
dc.DestroyClippingRegion()
- dc.SetClippingRegion(copy)
+ dc.SetClippingRegion(rect.CloneInflate(10, 0))
def _get_hyperlink_bitmap(self):
return get_bitmap(self.view_properties.get_hyperlink_icon())
@@ -292,7 +289,7 @@
def deflate_rect(rect, dx=INNER_PADDING, dy=INNER_PADDING):
- return Rect(*rect).Deflate(dx, dy)
+ return rect.CloneDeflate(dx, dy)
def center_point_with_offset(rect, dx=0, dy=0):
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py Fri Jul 11 12:15:03 2025 +0200
@@ -37,4 +37,4 @@
def deflate_rect(rect, dx=1, dy=1):
- return Rect(*rect).Deflate(dx, dy)
+ return rect.CloneDeflate(dx, dy)
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py Fri Jul 11 12:15:03 2025 +0200
@@ -123,4 +123,4 @@
def deflate_rect(rect, dx=1, dy=1):
- return Rect(*rect).Deflate(dx, dy)
+ return rect.CloneDeflate(dx, dy)
diff -r 72cfb98d5e1c -r c23d402b991b source/timelinelib/canvas/gc.py
--- a/source/timelinelib/canvas/gc.py Fri Jul 11 11:55:40 2025 +0200
+++ b/source/timelinelib/canvas/gc.py Fri Jul 11 12:15:03 2025 +0200
@@ -120,13 +120,13 @@
def left_half_of_rect(rect):
- r = Rect(*rect).Deflate(1, 1)
+ r = rect.CloneDeflate(1, 1)
r.SetWidth(r.GetWidth() // 2)
return r
def right_half_of_rect(rect):
- r = Rect(*rect).Deflate(1, 1)
+ r = rect.CloneDeflate(1, 1)
r.SetWidth(r.GetWidth() // 2)
r.SetPosition(wx.Point(r.GetX() + r.GetWidth(), r.GetY()))
return r
diff -r 72cfb98d5e1c -r c23d402b991b test/unit/canvas/drawing/rect.py
--- a/test/unit/canvas/drawing/rect.py Fri Jul 11 11:55:40 2025 +0200
+++ b/test/unit/canvas/drawing/rect.py Fri Jul 11 12:15:03 2025 +0200
@@ -27,3 +27,15 @@
inner_box_tuple = (10, 10, 10, 10)
self.assertTrue(box.Intersects(Rect(*inner_box_tuple)))
self.assertTrue(box.Intersects(inner_box_tuple))
+
+ def test_clone_and_inflate(self):
+ self.assertEqual(
+ Rect(0, 0, 10, 10).CloneInflate(1, 2),
+ Rect(-1, -2, 12, 14)
+ )
+
+ def test_clone_and_deflate(self):
+ self.assertEqual(
+ Rect(0, 0, 10, 10).CloneDeflate(1, 2),
+ Rect(1, 2, 8, 6)
+ )
changeset: 7999:1d3b1ab07169
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 11:22:15 2025 +0200
summary: Add Rect.CloneInflate and use it in _draw_container.
diff -r 9dc171f0d9e0 -r 1d3b1ab07169 source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:13:12 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:22:15 2025 +0200
@@ -505,7 +505,7 @@
self._draw_box(rect, event, view_properties)
def _draw_container(self, event, rect, view_properties):
- box_rect = Rect(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4)
+ box_rect = rect.CloneInflate(2, 2)
if EXTENDED_CONTAINER_HEIGHT.enabled():
box_rect = EXTENDED_CONTAINER_HEIGHT.get_vertical_larger_box_rect(rect)
self._draw_box(box_rect, event, view_properties)
diff -r 9dc171f0d9e0 -r 1d3b1ab07169 source/timelinelib/canvas/drawing/rect.py
--- a/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 11:13:12 2025 +0200
+++ b/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 11:22:15 2025 +0200
@@ -20,4 +20,8 @@
class Rect(wx.Rect):
- pass
+
+ def CloneInflate(self, dx, dy):
+ clone = Rect(self)
+ clone.Inflate(dx, dy)
+ return clone
changeset: 8000:546241b2cec8
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 11:37:43 2025 +0200
summary: There is no need to convert Rect.Intersects to a Rect object.
diff -r 1d3b1ab07169 -r 546241b2cec8 source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:22:15 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 11:37:43 2025 +0200
@@ -32,7 +32,6 @@
from wx import BRUSHSTYLE_TRANSPARENT
import timelinelib.wxgui.components.font as font
from timelinelib.canvas.drawing.drawers.ballondrawer import BallonDrawer
-from timelinelib.canvas.drawing.rect import Rect
OUTER_PADDING = 5 # Space between event boxes (pixels)
@@ -259,8 +258,12 @@
event.horizontal_mouse_position_factor = None
def get_events_in_rect(self, rect):
- wx_rect = Rect(*rect)
- return [event for (event, rect) in self.scene.event_data if rect.Intersects(wx_rect)]
+ return [
+ event
+ for (event, rect)
+ in self.scene.event_data
+ if rect.Intersects(rect)
+ ]
def _adjust_container_rect_for_hittest(self, rect):
if EXTENDED_CONTAINER_HEIGHT.enabled():
diff -r 1d3b1ab07169 -r 546241b2cec8 test/unit/canvas/drawing/rect.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unit/canvas/drawing/rect.py Fri Jul 11 11:37:43 2025 +0200
@@ -0,0 +1,29 @@
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
+#
+# This file is part of Timeline.
+#
+# Timeline 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.
+#
+# Timeline 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 Timeline. If not, see <http://www.gnu.org/licenses/>.
+
+
+from timelinelib.test.cases.unit import UnitTestCase
+from timelinelib.canvas.drawing.rect import Rect
+
+
+class describe_rect(UnitTestCase):
+
+ def test_intersects_can_take_both_rect_and_tuple(self):
+ box = Rect(0, 0, 100, 100)
+ inner_box_tuple = (10, 10, 10, 10)
+ self.assertTrue(box.Intersects(Rect(*inner_box_tuple)))
+ self.assertTrue(box.Intersects(inner_box_tuple))
changeset: 7998:9dc171f0d9e0
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 11:13:12 2025 +0200
summary: wx.DC.DrawPolygon expects integers.
diff -r 8f466597a40a -r 9dc171f0d9e0 source/timelinelib/canvas/gc.py
--- a/source/timelinelib/canvas/gc.py Fri Jul 11 10:17:31 2025 +0200
+++ b/source/timelinelib/canvas/gc.py Fri Jul 11 11:13:12 2025 +0200
@@ -100,7 +100,7 @@
self._points.append(self._create_point(x, y, radius, end_angle))
def _create_point(self, x, y, radius, angle):
- return x + radius * math.cos(angle), y + radius * math.sin(angle)
+ return int(x + radius * math.cos(angle)), int(y + radius * math.sin(angle))
def CloseSubpath(self):
if self._points:
changeset: 7997:8f466597a40a
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 11 10:17:31 2025 +0200
summary: Replace wx.Rect with subclass Rect that can attract more functionality.
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/drawing/drawers/ballondrawer.py
--- a/source/timelinelib/canvas/drawing/drawers/ballondrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/ballondrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -25,6 +25,7 @@
from timelinelib.canvas.gc import create_gc
import timelinelib.wxgui.components.font as font
from timelinelib.wxgui.utils import load_bitmap
+from timelinelib.canvas.drawing.rect import Rect
BALLOON_RADIUS = 12
ARROW_OFFSET = BALLOON_RADIUS + 25
@@ -200,7 +201,7 @@
by = top_y
bw = W + R + 1
bh = H + R + H_ARROW + 1
- bounding_rect = wx.Rect(bx, by, bw, bh)
+ bounding_rect = Rect(bx, by, bw, bh)
return bounding_rect, left_x + BALLOON_RADIUS, top_y + BALLOON_RADIUS
def draw_icon(self, x, y):
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Fri Jul 11 10:17:31 2025 +0200
@@ -32,6 +32,7 @@
from wx import BRUSHSTYLE_TRANSPARENT
import timelinelib.wxgui.components.font as font
from timelinelib.canvas.drawing.drawers.ballondrawer import BallonDrawer
+from timelinelib.canvas.drawing.rect import Rect
OUTER_PADDING = 5 # Space between event boxes (pixels)
@@ -258,7 +259,7 @@
event.horizontal_mouse_position_factor = None
def get_events_in_rect(self, rect):
- wx_rect = wx.Rect(*rect)
+ wx_rect = Rect(*rect)
return [event for (event, rect) in self.scene.event_data if rect.Intersects(wx_rect)]
def _adjust_container_rect_for_hittest(self, rect):
@@ -504,7 +505,7 @@
self._draw_box(rect, event, view_properties)
def _draw_container(self, event, rect, view_properties):
- box_rect = wx.Rect(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4)
+ box_rect = Rect(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4)
if EXTENDED_CONTAINER_HEIGHT.enabled():
box_rect = EXTENDED_CONTAINER_HEIGHT.get_vertical_larger_box_rect(rect)
self._draw_box(box_rect, event, view_properties)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/drawing/drawers/legenddrawer.py
--- a/source/timelinelib/canvas/drawing/drawers/legenddrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/legenddrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -24,6 +24,7 @@
import timelinelib.wxgui.components.font as font
from timelinelib.canvas.drawing.graphobject import GraphObject
from timelinelib.canvas.drawing.utils import darken_color
+from timelinelib.canvas.drawing.rect import Rect
# INNER_PADDING = 3 # Space inside event box to text (pixels)
@@ -94,7 +95,7 @@
def _draw_rectangle(self, go):
self._dc.SetBrush(go.brush_color)
self._dc.SetPen(go.pen_color)
- self._dc.DrawRectangle(wx.Rect(*go.rect))
+ self._dc.DrawRectangle(Rect(*go.rect))
def _create_graph_object(self):
tw, th = self._get_text_metrics(self._categories)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/drawing/rect.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/timelinelib/canvas/drawing/rect.py Fri Jul 11 10:17:31 2025 +0200
@@ -0,0 +1,23 @@
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
+#
+# This file is part of Timeline.
+#
+# Timeline 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.
+#
+# Timeline 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 Timeline. If not, see <http://www.gnu.org/licenses/>.
+
+
+import wx
+
+
+class Rect(wx.Rect):
+ pass
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/drawing/scene.py
--- a/source/timelinelib/canvas/drawing/scene.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/drawing/scene.py Fri Jul 11 10:17:31 2025 +0200
@@ -21,6 +21,7 @@
from timelinelib.canvas.drawing.utils import Metrics
from timelinelib.canvas.data import TimePeriod
from timelinelib.features.experimental.experimentalfeatures import EXTENDED_CONTAINER_STRATEGY
+from timelinelib.canvas.drawing.rect import Rect
FORWARD = 1
@@ -270,7 +271,6 @@
rh = self._calc_height_for_period_event(event)
rx = self._calc_x_pos_for_period_event(event)
ry = self._calc_y_pos_for_period_event(event)
- rect = wx.Rect(rx, ry, rw, rh)
return self._calc_ideal_wx_rect(rx, ry, rw, rh)
def _calc_width_for_period_event(self, event):
@@ -308,7 +308,7 @@
rx = self._calc_x_pos_for_non_period_event(event, rw)
ry = self._calc_y_pos_for_non_period_event(event, rh)
if event.is_milestone():
- rect = wx.Rect(rx, ry, rw, rh)
+ rect = Rect(rx, ry, rw, rh)
rect.SetWidth(rect.GetHeight())
rect.SetX(self._metrics.calc_x(event.get_time_period().start_time) - rw // 2)
return rect
@@ -362,7 +362,7 @@
right_edge_x = rx + rw
if right_edge_x > self.width + MARGIN:
rw = rw - right_edge_x + self.width + MARGIN
- return wx.Rect(rx, ry, rw, rh)
+ return Rect(rx, ry, rw, rh)
def _calc_strips_sizes_and_positions(self):
"""Fill the two arrays `minor_strip_data` and `major_strip_data`."""
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -26,6 +26,7 @@
from timelinelib.canvas.eventboxdrawers.defaultmilestonedrawer import DefaultMilestoneDrawer
from timelinelib.canvas.eventboxdrawers.handlerect import HandleRect, MIDDLE_HANDLE, LEFT_HANDLE, RIGHT_HANDLE
from timelinelib.wxgui.utils import load_bitmap
+from timelinelib.canvas.drawing.rect import Rect
HANDLE_SIZE = 4
@@ -59,7 +60,7 @@
dc.SetPen(black_solid_pen(1))
dc.DrawLine(x, y0, x, y1)
dc.DrawCircle(x, y1, 2)
- event.symbol_rect = wx.Rect(x - 5, y1 - 5, 10, 10)
+ event.symbol_rect = Rect(x - 5, y1 - 5, 10, 10)
def _draw_event_box(self, dc, rect, event, selected):
self._draw_background(dc, rect, event)
@@ -166,7 +167,7 @@
rx = 0
if w > self.scene.width:
w = self.scene.width
- return wx.Rect(int(rx), int(y), int(w), int(h))
+ return Rect(int(rx), int(y), int(w), int(h))
def _draw_balloon_indicator(self, dc, event, rect):
"""
@@ -245,7 +246,7 @@
def _draw_handles(self, dc, event, rect):
def draw_frame_around_event():
- small_rect = wx.Rect(*rect)
+ small_rect = Rect(*rect)
small_rect.Deflate(1, 1)
border_color = event.get_border_color()
border_color = darken_color(border_color)
@@ -275,7 +276,7 @@
@staticmethod
def _inflate_clipping_region(dc, rect):
- copy = wx.Rect(*rect)
+ copy = Rect(*rect)
copy.Inflate(10, 0)
dc.DestroyClippingRegion()
dc.SetClippingRegion(copy)
@@ -291,7 +292,7 @@
def deflate_rect(rect, dx=INNER_PADDING, dy=INNER_PADDING):
- return wx.Rect(*rect).Deflate(dx, dy)
+ return Rect(*rect).Deflate(dx, dy)
def center_point_with_offset(rect, dx=0, dy=0):
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/gradienteventboxdrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -22,6 +22,7 @@
from timelinelib.canvas.drawing.utils import lighten_color
from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import DefaultEventBoxDrawer
import timelinelib.meta.overrides as mark
+from timelinelib.canvas.drawing.rect import Rect
class GradientEventBoxDrawer(DefaultEventBoxDrawer):
@@ -36,4 +37,4 @@
def deflate_rect(rect, dx=1, dy=1):
- return wx.Rect(*rect).Deflate(dx, dy)
+ return Rect(*rect).Deflate(dx, dy)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/eventboxdrawers/handlerect.py
--- a/source/timelinelib/canvas/eventboxdrawers/handlerect.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/handlerect.py Fri Jul 11 10:17:31 2025 +0200
@@ -20,13 +20,14 @@
from timelinelib.canvas.drawing.utils import black_solid_pen
from timelinelib.canvas.drawing.utils import black_solid_brush
+from timelinelib.canvas.drawing.rect import Rect
LEFT_HANDLE = 'left'
MIDDLE_HANDLE = 'middle'
RIGHT_HANDLE = 'right'
-class HandleRect(wx.Rect):
+class HandleRect(Rect):
"""
This class represents the little squared rectangle showing up when an event
is selected. It's a handle with which you can resize or move the event.
@@ -35,7 +36,7 @@
SIZE = 4
def __init__(self, rect, pos=MIDDLE_HANDLE):
- wx.Rect.__init__(self, wx.Point(0, 0), wx.Size(self.SIZE, self.SIZE))
+ Rect.__init__(self, wx.Point(0, 0), wx.Size(self.SIZE, self.SIZE))
{LEFT_HANDLE: self._translate_to_left_edge,
MIDDLE_HANDLE: self._translate_to_middle,
RIGHT_HANDLE: self._translate_to_right_edge}[pos](rect)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py
--- a/source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/eventboxdrawers/othergradienteventboxdrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -23,6 +23,7 @@
from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import DefaultEventBoxDrawer
from timelinelib.canvas.gc import create_gc
import timelinelib.meta.overrides as mark
+from timelinelib.canvas.drawing.rect import Rect
GRADIENT_STYLE_LEFT = 1
@@ -122,4 +123,4 @@
def deflate_rect(rect, dx=1, dy=1):
- return wx.Rect(*rect).Deflate(dx, dy)
+ return Rect(*rect).Deflate(dx, dy)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/canvas/gc.py
--- a/source/timelinelib/canvas/gc.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/canvas/gc.py Fri Jul 11 10:17:31 2025 +0200
@@ -20,6 +20,8 @@
import wx
+from timelinelib.canvas.drawing.rect import Rect
+
class FakeGC:
@@ -55,7 +57,7 @@
# Otherwise if the alpha value of the Item(1).Colour is 0 it means that
# the event has a fuzzy left side.
self.dc.SetPen(wx.Pen(wx.WHITE, style=wx.PENSTYLE_TRANSPARENT))
- rect = wx.Rect(x, y, width, height)
+ rect = Rect(x, y, width, height)
color = self._gradient_stops.Item(1).Colour[:3]
transparent_colour = wx.Colour(*color, 0)
opaque_colour = wx.Colour(*color, 255)
@@ -118,13 +120,13 @@
def left_half_of_rect(rect):
- r = wx.Rect(*rect).Deflate(1, 1)
+ r = Rect(*rect).Deflate(1, 1)
r.SetWidth(r.GetWidth() // 2)
return r
def right_half_of_rect(rect):
- r = wx.Rect(*rect).Deflate(1, 1)
+ r = Rect(*rect).Deflate(1, 1)
r.SetWidth(r.GetWidth() // 2)
r.SetPosition(wx.Point(r.GetX() + r.GetWidth(), r.GetY()))
- return r
\ No newline at end of file
+ return r
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/features/experimental/experimentalfeaturecontainersize.py
--- a/source/timelinelib/features/experimental/experimentalfeaturecontainersize.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/features/experimental/experimentalfeaturecontainersize.py Fri Jul 11 10:17:31 2025 +0200
@@ -19,6 +19,7 @@
import wx
from timelinelib.features.experimental.experimentalfeature import ExperimentalFeature
from timelinelib.wxgui.components.font import Font
+from timelinelib.canvas.drawing.rect import Rect
CONFIG_NAME = "Extend Container height"
@@ -48,12 +49,12 @@
return OUTER_PAADING
def get_vertical_larger_box_rect(self, rect):
- return wx.Rect(rect.X - 2, rect.Y - 2 - PADDING, rect.Width + 4, rect.Height + 4 + PADDING)
+ return Rect(rect.X - 2, rect.Y - 2 - PADDING, rect.Width + 4, rect.Height + 4 + PADDING)
def draw_container_text_top_adjusted(self, text, dc, rect):
old_font = dc.GetFont()
dc.SetFont(Font(FONT_SIZE))
- dc.SetClippingRegion(wx.Rect(rect.X, rect.Y + Y_OFFSET, rect.Width, rect.Height))
+ dc.SetClippingRegion(Rect(rect.X, rect.Y + Y_OFFSET, rect.Width, rect.Height))
text_x = rect.X + INNER_PADDING
text_y = rect.Y + INNER_PADDING + TEXT_OFFSET
dc.DrawText(text, text_x, text_y)
diff -r a01f67e463eb -r 8f466597a40a source/timelinelib/wxgui/components/categorytree.py
--- a/source/timelinelib/wxgui/components/categorytree.py Thu Jul 10 11:02:13 2025 +0200
+++ b/source/timelinelib/wxgui/components/categorytree.py Fri Jul 11 10:17:31 2025 +0200
@@ -27,6 +27,7 @@
from timelinelib.wxgui.dialogs.editcategory.view import EditCategoryDialog
from timelinelib.canvas.data.immutable import InvalidOperationError
import timelinelib.wxgui.utils as gui_utils
+from timelinelib.canvas.drawing.rect import Rect
class CustomCategoryTree(wx.ScrolledWindow):
@@ -326,10 +327,10 @@
def _render_checkbox(self, item):
color = item.get("color", None)
(w, h) = (17, 17)
- bounding_rect = wx.Rect(item["x"] + self.model.INDENT_PX,
- item["y"] + (self.model.ITEM_HEIGHT_PX - h) // 2,
- w,
- h)
+ bounding_rect = Rect(item["x"] + self.model.INDENT_PX,
+ item["y"] + (self.model.ITEM_HEIGHT_PX - h) // 2,
+ w,
+ h)
if item["visible"]:
flag = wx.CONTROL_CHECKED
else:
@@ -543,4 +544,4 @@
def _save_category_parent(self, category, parent):
category.parent = parent
- category.save()
\ No newline at end of file
+ category.save()
diff -r a01f67e463eb -r 8f466597a40a test/specs/wxgui/components/canvas/noop.py
--- a/test/specs/wxgui/components/canvas/noop.py Thu Jul 10 11:02:13 2025 +0200
+++ b/test/specs/wxgui/components/canvas/noop.py Fri Jul 11 10:17:31 2025 +0200
@@ -26,6 +26,7 @@
from timelinelib.wxgui.components.maincanvas.noop import NoOpInputHandler
from timelinelib.wxgui.cursor import Cursor
from timelinelib.wxgui.keyboard import Keyboard
+from timelinelib.canvas.drawing.rect import Rect
class NoOpInputHandlerSpec(UnitTestCase):
@@ -34,7 +35,7 @@
event = an_event()
time = human_time_to_gregorian("1 Jan 2011")
self.given_time_at_x_is(10, time)
- self.given_event_with_rect_at(Cursor(10, 10), event, wx.Rect(0, 0, 20, 20))
+ self.given_event_with_rect_at(Cursor(10, 10), event, Rect(0, 0, 20, 20))
self.given_event_selected(event)
self.canvas.HitResizeHandle.return_value = None
self.canvas.HitMoveHandle.return_value = True
@@ -45,7 +46,7 @@
event = an_event_with(ends_today=True)
time = human_time_to_gregorian("1 Jan 2011")
self.given_time_at_x_is(10, time)
- self.given_event_with_rect_at(Cursor(10, 10), event, wx.Rect(0, 0, 20, 20))
+ self.given_event_with_rect_at(Cursor(10, 10), event, Rect(0, 0, 20, 20))
self.given_event_selected(event)
self.handler.left_mouse_down(Cursor(10, 10), Keyboard(False, False, False))
self.assertEqual(0, self.canvas.SetInputHandler.call_count)
@@ -54,7 +55,7 @@
event = an_event_with(ends_today=True)
time = human_time_to_gregorian("1 Jan 2011")
self.given_time_at_x_is(10, time)
- self.given_event_with_rect_at(Cursor(10, 10), event, wx.Rect(0, 0, 20, 20))
+ self.given_event_with_rect_at(Cursor(10, 10), event, Rect(0, 0, 20, 20))
self.given_event_selected(event)
self.handler.mouse_moved(Cursor(10, 10), Keyboard(False, False, False))
self.assertEqual(0, self.canvas._set_move_cursor.call_count)
diff -r a01f67e463eb -r 8f466597a40a test/specs/wxgui/components/canvas/timelinecanvascontroller.py
--- a/test/specs/wxgui/components/canvas/timelinecanvascontroller.py Thu Jul 10 11:02:13 2025 +0200
+++ b/test/specs/wxgui/components/canvas/timelinecanvascontroller.py Fri Jul 11 10:17:31 2025 +0200
@@ -29,6 +29,7 @@
from timelinelib.test.utils import human_time_to_gregorian
from timelinelib.wxgui.components.timelinepanelguicreator import InputHandlerState
from timelinelib.config.dotfile import Config
+from timelinelib.canvas.drawing.rect import Rect
ANY_Y = 0
@@ -76,7 +77,7 @@
if description is not None:
event.set_data("description", description)
self.db.save_event(event)
- self.mock_drawer.events_and_rects.append((event, wx.Rect(pos[0], pos[1], size[0], size[1])))
+ self.mock_drawer.events_and_rects.append((event, Rect(pos[0], pos[1], size[0], size[1])))
return event
def given_time_at_x_is(self, x, time):
diff -r a01f67e463eb -r 8f466597a40a test/unit/canvas/eventboxdrawers/defaulteventboxdrawer.py
--- a/test/unit/canvas/eventboxdrawers/defaulteventboxdrawer.py Thu Jul 10 11:02:13 2025 +0200
+++ b/test/unit/canvas/eventboxdrawers/defaulteventboxdrawer.py Fri Jul 11 10:17:31 2025 +0200
@@ -23,6 +23,7 @@
from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import DefaultEventBoxDrawer
from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import INNER_PADDING
from timelinelib.canvas.eventboxdrawers.defaultmilestonedrawer import DefaultMilestoneDrawer
+from timelinelib.canvas.drawing.rect import Rect
DEFAULT_TEXT = "test"
@@ -31,61 +32,61 @@
class describe_default_exventbox_drawer_draw_text(UnitTestCase):
def test_when_rect_has_zero_width_text_is_not_drawn(self):
- rect = wx.Rect(0, 0, 0, 0)
+ rect = Rect(0, 0, 0, 0)
self.drawer._draw_text(self.dc, rect, self.event)
self.assertEqual(self.dc.DrawText.call_count, 0)
def test_when_rect_has_inner_padding_width_text_is_not_drawn(self):
- rect = wx.Rect(0, 0, INNER_PADDING * 2, 0)
+ rect = Rect(0, 0, INNER_PADDING * 2, 0)
self.drawer._draw_text(self.dc, rect, self.event)
self.assertEqual(self.dc.DrawText.call_count, 0)
def test_non_centered_text_is_left_aligned(self):
WIDTH = 100
HEIGHT = 20
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.drawer.center_text = False
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with(DEFAULT_TEXT, INNER_PADDING, INNER_PADDING)
def test_centered_text_is_not_left_aligned(self):
WIDTH = 100
HEIGHT = 20
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.drawer.center_text = True
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with(DEFAULT_TEXT, (WIDTH - 2 * INNER_PADDING - 50) // 2 + INNER_PADDING, INNER_PADDING)
def test_centered_text_is_left_aligned_if_text_is_too_long_to_fit(self):
WIDTH = 100
HEIGHT = 20
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.dc.GetTextExtent.return_value = (500, 0)
self.drawer.center_text = True
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with(DEFAULT_TEXT, INNER_PADDING, INNER_PADDING)
def test_non_centered_text_is_left_ajusted_when_fuzzy(self):
WIDTH = 100
HEIGHT = 20
self.event.has_edge_icons.return_value = True
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.drawer.center_text = False
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with(DEFAULT_TEXT, INNER_PADDING + HEIGHT // 2, INNER_PADDING)
def test_non_centered_text_is_left_ajusted_when_locked(self):
WIDTH = 100
HEIGHT = 20
self.event.has_edge_icons.return_value = True
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.drawer.center_text = False
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with(DEFAULT_TEXT, INNER_PADDING + HEIGHT // 2, INNER_PADDING)
def test_if_checkmark_is_to_be_used_it_is_placed_as_first_char_in_text(self):
@@ -93,11 +94,11 @@
HEIGHT = 20
self.event.has_edge_icons.return_value = True
self.event.get_progress.return_value = 100
- rect = wx.Rect(0, 0, WIDTH, HEIGHT)
+ rect = Rect(0, 0, WIDTH, HEIGHT)
self.drawer.center_text = False
self.drawer.view_properties.get_display_checkmark_on_events_done.return_value = True
self.drawer._draw_text(self.dc, rect, self.event)
- self.dc.SetClippingRegion.assert_called_with(wx.Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
+ self.dc.SetClippingRegion.assert_called_with(Rect(INNER_PADDING, INNER_PADDING, WIDTH - 2 * INNER_PADDING, HEIGHT - 2 * INNER_PADDING))
self.dc.DrawText.assert_called_with("\u2714" + DEFAULT_TEXT, INNER_PADDING + HEIGHT // 2, INNER_PADDING)
def test_milestone_with_no_text_can_be_drawn(self):
@@ -105,7 +106,7 @@
self.event.text = ""
self.event.get_color.return_value = (127, 127, 127)
self.dc.GetTextExtent.return_value = wx.Size(10, 10)
- rect = wx.Rect(0, 0, 100, 20)
+ rect = Rect(0, 0, 100, 20)
scene = Mock()
try:
DefaultMilestoneDrawer(rect, self.event, False, self.drawer.view_properties).draw(self.dc)
changeset: 7996:a01f67e463eb
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Thu Jul 10 11:02:13 2025 +0200
summary: Show balloon for period events shown as points below the divider line.
diff -r 7f208da5f70c -r a01f67e463eb documentation/changelog.rst
--- a/documentation/changelog.rst Tue Jul 08 10:29:58 2025 +0200
+++ b/documentation/changelog.rst Thu Jul 10 11:02:13 2025 +0200
@@ -34,6 +34,10 @@
* ``Corrupted icon file causes exception``
Use a default icon when icon file is corrupted and disable error dialog.
+* Balloons are now shown for period events that are shown as points below the
+ divider line if preference "Never show period Events as point Events" is
+ checked.
+
Windows installer:
* The Windows installer is now built on Linux/Wine and only for 64 bit platforms.
diff -r 7f208da5f70c -r a01f67e463eb source/timelinelib/canvas/drawing/drawers/default.py
--- a/source/timelinelib/canvas/drawing/drawers/default.py Tue Jul 08 10:29:58 2025 +0200
+++ b/source/timelinelib/canvas/drawing/drawers/default.py Thu Jul 10 11:02:13 2025 +0200
@@ -544,6 +544,8 @@
def _draw_ballon(self, event, event_rect, sticky):
"""Draw one ballon on a selected event that has 'description' data."""
ballon_drawer = BallonDrawer(self.dc, self.scene, self.appearance, event)
+ if hasattr(event, 'symbol_rect'):
+ event_rect = event.symbol_rect
self.balloon_data.append(ballon_drawer.draw(event_rect, sticky))
def get_period_xpos(self, time_period):
changeset: 7995:7f208da5f70c
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:29:58 2025 +0200
summary: Ignore build artifacts.
diff -r 63ba50e3d1f4 -r 7f208da5f70c .hgignore
--- a/.hgignore Tue Jul 08 10:10:08 2025 +0200
+++ b/.hgignore Tue Jul 08 10:29:58 2025 +0200
@@ -1,5 +1,8 @@
syntax: glob
*.bak
+Dockerfile*.ci.files
+timeline-*-Win64Setup.exe
+timeline-*.zip
mikado.png
mikado.pdf