hg clone static-http://projects.rickardlindberg.me/scm/timeline
changeset: 8005:25c17562e3f3
tag: tip
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
changeset: 7994:63ba50e3d1f4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:10:08 2025 +0200
summary: Run tests in Windows environment as well.
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Sun Jul 06 09:10:31 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Tue Jul 08 10:10:08 2025 +0200
@@ -5,6 +5,7 @@
ENV PYTHON_DIR=C:\\Python
ENV PYTHON_PIP_EXE=C:\\Python\\Scripts\\pip.exe
ENV INNO_DIR=C:\\Inno
+ENV GETTEXT_DIR=C:\\gettext
RUN dnf update -y
RUN dnf install -y wine
@@ -16,6 +17,7 @@
RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
+RUN wget https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.25-v1.17/gettext0.25-iconv1.17-static-64.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
@@ -27,6 +29,7 @@
&& wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \
&& wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$INNO_DIR \
+ && wine gettext0.25-iconv1.17-static-64.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$GETTEXT_DIR \
&& wineserver -w'
CMD ["xvfb-run", "python", "tools/build-windows-exe-from-source-zip.py", "--output-list", "Dockerfile.zip-to-exe.ci.files"]
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/dialogs/preferences/view.py
--- a/source/timelinelib/wxgui/dialogs/preferences/view.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/dialogs/preferences/view.py Tue Jul 08 10:10:08 2025 +0200
@@ -468,7 +468,7 @@
"date_time_text": _("Date && Time"),
"week_start_text": _("Week start on:"),
"week_start_choices": [_("Monday"), _("Sunday")],
- "workday_length_text": f'{_("Workday Length in hours")}:',
+ "workday_length_text": _("Workday Length in hours:"),
"fonts_text": _("Fonts"),
"colours_text": _("Colours"),
"major_strip_text": _("Major Strips:"),
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/frames/mainframe/lockhandler.py
--- a/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Tue Jul 08 10:10:08 2025 +0200
@@ -110,7 +110,7 @@
return f"{self._path}.lock"
def _report_other_process_uses_lockfile(self, lockpath):
- message = _(f"""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
+ message = _("""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
This lockfile must be removed in order be able to continue editing the timeline!
- The lockfile is found at: {lockpath}""")
+ The lockfile is found at: {lockpath}""").format(lockpath=lockpath)
display_warning_message(message, self._main_frame)
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 09:10:31 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Tue Jul 08 10:10:08 2025 +0200
@@ -51,6 +51,11 @@
if len(zips) != 1:
sys.exit("ERROR: Could not find source zip.")
archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
+ subprocess.check_call([
+ "wine",
+ f"{environ['PYTHON_DIR']}\\python.exe",
+ archive.get_path("tools/execute-specs.py"),
+ ])
archive.change_constant(
"tools/winbuildtools/inno/timeline.iss",
"AppVerName",
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 translations/timeline.pot
--- a/translations/timeline.pot Sun Jul 06 09:10:31 2025 +0200
+++ b/translations/timeline.pot Tue Jul 08 10:10:08 2025 +0200
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-04-06 23:09+0200\n"
+"POT-Creation-Date: 2025-07-08 07:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -3076,6 +3076,10 @@
msgid "Sunday"
msgstr ""
+#: source\timelinelib\wxgui\dialogs\preferences\view.py:471
+msgid "Workday Length in hours:"
+msgstr ""
+
#: source\timelinelib\wxgui\dialogs\preferences\view.py:472
msgid "Fonts"
msgstr ""
changeset: 7994:63ba50e3d1f4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:10:08 2025 +0200
summary: Run tests in Windows environment as well.
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Sun Jul 06 09:10:31 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Tue Jul 08 10:10:08 2025 +0200
@@ -5,6 +5,7 @@
ENV PYTHON_DIR=C:\\Python
ENV PYTHON_PIP_EXE=C:\\Python\\Scripts\\pip.exe
ENV INNO_DIR=C:\\Inno
+ENV GETTEXT_DIR=C:\\gettext
RUN dnf update -y
RUN dnf install -y wine
@@ -16,6 +17,7 @@
RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
+RUN wget https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.25-v1.17/gettext0.25-iconv1.17-static-64.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
@@ -27,6 +29,7 @@
&& wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \
&& wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$INNO_DIR \
+ && wine gettext0.25-iconv1.17-static-64.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$GETTEXT_DIR \
&& wineserver -w'
CMD ["xvfb-run", "python", "tools/build-windows-exe-from-source-zip.py", "--output-list", "Dockerfile.zip-to-exe.ci.files"]
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/dialogs/preferences/view.py
--- a/source/timelinelib/wxgui/dialogs/preferences/view.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/dialogs/preferences/view.py Tue Jul 08 10:10:08 2025 +0200
@@ -468,7 +468,7 @@
"date_time_text": _("Date && Time"),
"week_start_text": _("Week start on:"),
"week_start_choices": [_("Monday"), _("Sunday")],
- "workday_length_text": f'{_("Workday Length in hours")}:',
+ "workday_length_text": _("Workday Length in hours:"),
"fonts_text": _("Fonts"),
"colours_text": _("Colours"),
"major_strip_text": _("Major Strips:"),
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/frames/mainframe/lockhandler.py
--- a/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Tue Jul 08 10:10:08 2025 +0200
@@ -110,7 +110,7 @@
return f"{self._path}.lock"
def _report_other_process_uses_lockfile(self, lockpath):
- message = _(f"""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
+ message = _("""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
This lockfile must be removed in order be able to continue editing the timeline!
- The lockfile is found at: {lockpath}""")
+ The lockfile is found at: {lockpath}""").format(lockpath=lockpath)
display_warning_message(message, self._main_frame)
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 09:10:31 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Tue Jul 08 10:10:08 2025 +0200
@@ -51,6 +51,11 @@
if len(zips) != 1:
sys.exit("ERROR: Could not find source zip.")
archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
+ subprocess.check_call([
+ "wine",
+ f"{environ['PYTHON_DIR']}\\python.exe",
+ archive.get_path("tools/execute-specs.py"),
+ ])
archive.change_constant(
"tools/winbuildtools/inno/timeline.iss",
"AppVerName",
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 translations/timeline.pot
--- a/translations/timeline.pot Sun Jul 06 09:10:31 2025 +0200
+++ b/translations/timeline.pot Tue Jul 08 10:10:08 2025 +0200
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-04-06 23:09+0200\n"
+"POT-Creation-Date: 2025-07-08 07:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -3076,6 +3076,10 @@
msgid "Sunday"
msgstr ""
+#: source\timelinelib\wxgui\dialogs\preferences\view.py:471
+msgid "Workday Length in hours:"
+msgstr ""
+
#: source\timelinelib\wxgui\dialogs\preferences\view.py:472
msgid "Fonts"
msgstr ""