diff --git a/examples/demo.py b/examples/demo.py index ece86ce..0feb3e8 100755 --- a/examples/demo.py +++ b/examples/demo.py @@ -17,7 +17,7 @@ import cairo import gi from examples.exampleitems import Box, Circle, FatLine, PortoBox, Text -from gaphas import Canvas, GtkView, View, state +from gaphas import Canvas, GtkView, state from gaphas.canvas import Context from gaphas.item import Line from gaphas.painter import ( @@ -112,13 +112,13 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex view = GtkView() view.painter = ( PainterChain() - .append(FreeHandPainter(ItemPainter(view))) + .append(FreeHandPainter(ItemPainter(view.selection))) .append(HandlePainter(view)) .append(FocusedItemPainter(view)) .append(ToolPainter(view)) ) view.bounding_box_painter = BoundingBoxPainter( - FreeHandPainter(ItemPainter(view)), view.bounding_box_updater + FreeHandPainter(ItemPainter(view.selection)), view.bounding_box_updater ) w = Gtk.Window() w.set_title(title) @@ -236,25 +236,26 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex b = Gtk.Button.new_with_label("Write demo.png") def on_write_demo_png_clicked(button): - svgview = View(view.canvas) - svgview.painter = ItemPainter(svgview) + painter = ItemPainter() # Update bounding boxes with a temporary CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) - svgview.update_bounding_box(tmpcr) + bounding_box = ( + BoundingBoxPainter(painter) + .paint(canvas.get_all_items(), tmpcr) + .bounding_box + ) tmpcr.show_page() tmpsurface.flush() - w, h = svgview.bounding_box.width, svgview.bounding_box.height - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h)) + surface = cairo.ImageSurface( + cairo.FORMAT_ARGB32, int(bounding_box.width), int(bounding_box.height) + ) cr = cairo.Context(surface) - svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) - cr.save() - paint(svgview, cr) - - cr.restore() + cr.translate(-bounding_box.x, -bounding_box.y) + painter.paint(items=view.canvas.get_all_items(), cairo=cr) cr.show_page() surface.write_to_png("demo.png") @@ -264,22 +265,26 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex b = Gtk.Button.new_with_label("Write demo.svg") def on_write_demo_svg_clicked(button): - svgview = View(view.canvas) - svgview.painter = ItemPainter(svgview) + painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) - svgview.update_bounding_box(tmpcr) + bounding_box = ( + BoundingBoxPainter(painter) + .paint(canvas.get_all_items(), tmpcr) + .bounding_box + ) tmpcr.show_page() tmpsurface.flush() - w, h = svgview.bounding_box.width, svgview.bounding_box.height - surface = cairo.SVGSurface("demo.svg", w, h) + surface = cairo.SVGSurface( + "demo.svg", int(bounding_box.width), int(bounding_box.height) + ) cr = cairo.Context(surface) - svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) - paint(svgview, cr) + cr.translate(-bounding_box.x, -bounding_box.y) + painter.paint(items=view.canvas.get_all_items(), cairo=cr) cr.show_page() surface.flush() surface.finish() diff --git a/gaphas/painter/__init__.py b/gaphas/painter/__init__.py index 183a315..ff1e191 100644 --- a/gaphas/painter/__init__.py +++ b/gaphas/painter/__init__.py @@ -12,7 +12,7 @@ def DefaultPainter(view) -> Painter: """Default painter, containing item, handle and tool painters.""" return ( PainterChain() - .append(ItemPainter(view)) + .append(ItemPainter(view.selection)) .append(HandlePainter(view)) .append(FocusedItemPainter(view)) .append(ToolPainter(view)) diff --git a/gaphas/painter/boundingboxpainter.py b/gaphas/painter/boundingboxpainter.py index 3d12e10..9466546 100644 --- a/gaphas/painter/boundingboxpainter.py +++ b/gaphas/painter/boundingboxpainter.py @@ -98,10 +98,21 @@ class BoundingBoxPainter: def __init__( self, item_painter: ItemPainterType, - bounding_box_updater: Callable[[Item, Rectangle], None], + bounding_box_updater: Optional[Callable[[Item, Rectangle], None]] = None, ): self.item_painter = item_painter - self.bounding_box_updater = bounding_box_updater + if bounding_box_updater: + self.bounding_box_updater = bounding_box_updater + else: + self.bounding_box = Rectangle() + + def default_bounding_box_updater(item, bounds): + if not self.bounding_box: + self.bounding_box = Rectangle(*bounds) + else: + self.bounding_box += bounds + + self.bounding_box_updater = default_bounding_box_updater def paint_item(self, item, cairo): cairo = CairoBoundingBoxContext(cairo) @@ -118,7 +129,8 @@ class BoundingBoxPainter: bounds.expand(1) self.bounding_box_updater(item, bounds) - def paint(self, items: Sequence[Item], cairo): + def paint(self, items: Sequence[Item], cairo) -> BoundingBoxPainter: """Draw the items.""" for item in items: self.paint_item(item, cairo) + return self diff --git a/gaphas/painter/itempainter.py b/gaphas/painter/itempainter.py index 4fd21e4..d495040 100644 --- a/gaphas/painter/itempainter.py +++ b/gaphas/painter/itempainter.py @@ -1,9 +1,10 @@ -from typing import Sequence +from typing import Optional, Sequence from cairo import LINE_JOIN_ROUND from gaphas.canvas import Context from gaphas.item import Item +from gaphas.view.selection import Selection # The tolerance for Cairo. Bigger values increase speed and reduce accuracy # (default: 0.1) @@ -25,25 +26,26 @@ class ItemPainter: draw_all = False - def __init__(self, view=None): - assert view - self.view = view + def __init__(self, selection: Optional[Selection] = None): + self.selection = selection def paint_item(self, item, cairo): - view = self.view cairo.save() try: cairo.transform(item.matrix_i2c.to_cairo()) + selection = self.selection + if not selection: + selection = Selection() item.draw( DrawContext( painter=self, cairo=cairo, _item=item, - selected=(item in view.selection.selected_items), - focused=(item is view.selection.focused_item), - hovered=(item is view.selection.hovered_item), - dropzone=(item is view.selection.dropzone_item), + selected=(item in selection.selected_items), + focused=(item is selection.focused_item), + hovered=(item is selection.hovered_item), + dropzone=(item is selection.dropzone_item), draw_all=self.draw_all, ) ) diff --git a/gaphas/view/view.py b/gaphas/view/view.py index 7a38202..33d81a6 100644 --- a/gaphas/view/view.py +++ b/gaphas/view/view.py @@ -15,7 +15,7 @@ class View: self._matrix = Matrix() self._painter: Painter = DefaultPainter(self) self._bounding_box_painter: Painter = BoundingBoxPainter( - ItemPainter(self), self.bounding_box_updater + ItemPainter(self.selection), self.bounding_box_updater # type: ignore[attr-defined] ) self._qtree: Quadtree[Item, Tuple[float, float, float, float]] = Quadtree() diff --git a/tests/test_segment.py b/tests/test_segment.py index 588429a..1f946a6 100644 --- a/tests/test_segment.py +++ b/tests/test_segment.py @@ -4,7 +4,7 @@ import pytest from gaphas.canvas import Canvas from gaphas.item import Item from gaphas.segment import HandleFinder, Line, Segment, SegmentHandleFinder -from gaphas.view import View +from gaphas.view import GtkView class SegmentFixture: @@ -12,7 +12,7 @@ class SegmentFixture: self.canvas = Canvas() self.line = Line() self.canvas.add(self.line) - self.view = View(self.canvas) + self.view = GtkView(self.canvas) self.item = Item() diff --git a/tests/test_view.py b/tests/test_view.py index fd3529e..eca93c3 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -6,7 +6,7 @@ from gi.repository import Gtk from gaphas.canvas import Canvas from gaphas.item import Element as Box -from gaphas.view import GtkView, View +from gaphas.view import GtkView class ViewFixture: @@ -91,14 +91,6 @@ def test_item_removal(view_fixture): def test_view_registration(view_fixture): canvas = Canvas() - # Simple views do not register on the canvas - - view = View(canvas) - assert len(canvas._registered_views) == 0 - - box = Box() - canvas.add(box) - # GTK view does register for updates though view = GtkView(canvas)