# Wx::SF::RectShape - rectangle shape class # Copyright (c) M.J.N. Corino, The Netherlands module Wx::SF class RectShape < Shape # default values module DEFAULT class << self # Default value of RectShape fill data member. def fill; fill ||= Wx::WHITE_BRUSH.dup; end # Default value of RectShape @border data member. def border; @border ||= Wx::BLACK_PEN.dup; end end # Default value of RectShape @rect_size data member. SIZE = Wx::RealPoint.new(100, 50) end property rect_size: :serialize_rect_size property({ fill: :serialize_rect_fill, border: :serialize_rect_border }, optional: true) # Constructor. # @param [Wx::RealPoint,Wx::Point] pos Initial position # @param [Wx::RealPoint,Wx::Size,Wx::Point,Array(Float,Float)] size Initial size # @param [Wx::SF::Diagram] diagram parent diagram def initialize(pos = Shape::DEFAULT::POSITION, size = DEFAULT::SIZE, diagram: nil) super(pos, diagram: diagram) set_rect_size(size.to_real_point) @fill = nil @border = nil end # Set rectangle's fill style. # @overload set_fill(brush) # @param [Wx::Brush] brush # @overload set_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID) # @param [Wx::Colour,Symbol,String] color brush color # @param [Wx::BrushStyle] style # @overload set_fill(stipple_bitmap) # @param [Wx::Bitmap] stipple_bitmap def set_fill(*args) @fill = if args.size == 1 && Wx::Brush === args.first args.first else Wx::Brush.new(*args) end end alias :fill= :set_fill # Get current fill style. # @return [Wx::Brush] Current brush def get_fill @fill || (@diagram&.shape_canvas ? @diagram.shape_canvas.fill_brush : DEFAULT.fill) end alias :fill :get_fill # Set rectangle's border style. # @overload set_border(pen) # @param [Wx::Pen] pen # @overload set_border(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID) # @param [Wx::Colour,String,Symbol] color # @param [Integer] width # @param [Wx::PenStyle] style def set_border(*args) @border = if args.size == 1 && Wx::Pen === args.first args.first else Wx::Pen.new(*args) end end alias :border= :set_border # Get current border style. # @return [Wx::Pen] Current pen def get_border @border || (@diagram&.shape_canvas ? @diagram.shape_canvas.border_pen : DEFAULT.border) end alias :border :get_border # Set the rectangle size. # @overload set_rect_size(x, y) # @param [Float] x Horizontal size # @param [Float] y Vertical size # @overload set_rect_size(size) # @param [Wx::RealPoint,Array(Float, Float)] size New size def set_rect_size(*args) x, y = args.size == 1 ? args.first.to_real_point : args # set new size while preventing 'invisible' shapes @rect_size = Wx::RealPoint.new([1.0, x].max, [1.0, y].max) end alias :rect_size= :set_rect_size # Get the rectangle size. # @return [Wx::RealPoint] Current size def get_rect_size @rect_size end alias :rect_size :get_rect_size # Get shape's bounding box. The function can be overridden if necessary. # @return [Wx::Rect] Bounding rectangle def get_bounding_box apos = get_absolute_position Wx::Rect.new([apos.x.to_i, apos.y.to_i], [@rect_size.x.to_i, @rect_size.y.to_i]) end # Get intersection point of the shape border and a line leading from # 'start' point to 'end' point. The function can be overridden if necessary. # @param [Wx::RealPoint] start Starting point of the virtual intersection line # @param [Wx::RealPoint] end_pt Ending point of the virtual intersection line # @return [Wx::RealPoint] Intersection point def get_border_point(start, end_pt) # HINT: override it for custom actions ... start = start.to_real_point; end_pt.to_real_point # the function calculates intersection of line leading from the shape center to # given point with the shape's bounding box bb_rct = get_bounding_box intersection = Shape.lines_intersection(bb_rct.top_left.to_real, Wx::RealPoint.new(bb_rct.top_right.x + 1.0, bb_rct.top_right.y.to_f), start, end_pt) intersection ||= Shape.lines_intersection(Wx::RealPoint.new(bb_rct.top_right.x + 1.0, bb_rct.top_right.y.to_f), Wx::RealPoint.new(bb_rct.bottom_right.x + 1.0, bb_rct.bottom_right.y + 1.0), start, end_pt) intersection ||= Shape.lines_intersection(Wx::RealPoint.new(bb_rct.bottom_right.x + 1.0, bb_rct.bottom_right.y + 1.0), Wx::RealPoint.new(bb_rct.bottom_left.x.to_f, bb_rct.bottom_left.y + 1.0), start, end_pt) intersection ||= Shape.lines_intersection(Wx::RealPoint.new(bb_rct.bottom_left.x.to_f, bb_rct.bottom_left.y + 1), bb_rct.top_left.to_real, start, end_pt) intersection || get_center end # Function called by the framework responsible for creation of shape handles # at the creation time. The function can be overridden if necessary. def create_handles # HINT: overload it for custom actions... add_handle(Shape::Handle::TYPE::LEFTTOP) add_handle(Shape::Handle::TYPE::TOP) add_handle(Shape::Handle::TYPE::RIGHTTOP) add_handle(Shape::Handle::TYPE::RIGHT) add_handle(Shape::Handle::TYPE::RIGHTBOTTOM) add_handle(Shape::Handle::TYPE::BOTTOM) add_handle(Shape::Handle::TYPE::LEFTBOTTOM) add_handle(Shape::Handle::TYPE::LEFT) add_handle(Shape::Handle::TYPE::LEFTTOP) end # Event handler called during dragging of the shape handle. # The function can be overridden if necessary. # # The function is called by the framework (by the shape canvas). # @param [Shape::Handle] handle Reference to dragged handle def on_handle(handle) # HINT: overload it for custom actions... do_on_handle(handle) super end # Event handler called when the user started to drag the shape handle. # The function can be overridden if necessary. # # The function is called by the framework (by the shape canvas). # @param [Shape::Handle] handle Reference to dragged handle def on_begin_handle(handle) do_begin_handle super end # Resize the shape to bound all child shapes. The function can be overridden if necessary. def fit_to_children # HINT: overload it for custom actions... # get bounding box of the shape and children set to be inside it ch_bb = get_bounding_box shp_bb = ch_bb.dup @child_shapes.each do |child| if child.has_style?(STYLE::ALWAYS_INSIDE) child.get_complete_bounding_box(ch_bb, BBMODE::SELF | BBMODE::CHILDREN) end end unless ch_bb.empty? unless shp_bb.contains?(ch_bb) dx = ch_bb.left - shp_bb.left dy = ch_bb.top - shp_bb.top # resize parent shape shp_bb.union!(ch_bb) move_to(shp_bb.get_position.x, shp_bb.get_position.y) @rect_size = Wx::RealPoint.new(shp_bb.get_size.x.to_f, shp_bb.get_size.y.to_f) if has_style?(STYLE::EMIT_EVENTS) evt = ShapeEvent.new(EVT_SF_SHAPE_SIZE_CHANGED, self.object_id) evt.set_shape(self) get_parent_canvas.get_event_handler.process_event(evt) end # move its "1st level" children if necessary if dx < 0 || dy < 0 @child_shapes.each do |child| child.move_by(dx.to_i.abs, 0) if dx < 0 child.move_by(0, dy.to_i.abs) if dy < 0 end end end end end # Scale the shape size by in both directions. The function can be overridden if necessary # (new implementation should call default one ore scale shape's children manually if necessary). # @param [Float] x Horizontal scale factor # @param [Float] y Vertical scale factor # @param [Boolean] children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via update() function. def scale(x, y, children: WITHCHILDREN) # HINT: overload it for custom actions... if x > 0 && y > 0 scale_rectangle(x, y) # call default function implementation (needed for scaling of shape's children) super end end protected # Handle action at handle drag beginning def do_begin_handle # noop end # Scale the rectangle size for this shape. # @param [Float] x Horizontal scale factor # @param [Float] y Vertical scale factor def scale_rectangle(x, y) set_rect_size(@rect_size.x * x, @rect_size.y * y) end # Handle's shape specific actions on handling handle events. # The function can be overridden if necessary. # @param [Shape::Handle] handle Reference to dragged handle # @see #on_handle def do_on_handle(handle) case handle.type when Shape::Handle::TYPE::LEFT on_left_handle(handle) when Shape::Handle::TYPE::LEFTTOP on_left_handle(handle) on_top_handle(handle) when Shape::Handle::TYPE::LEFTBOTTOM on_left_handle(handle) on_bottom_handle(handle) when Shape::Handle::TYPE::RIGHT on_right_handle(handle) when Shape::Handle::TYPE::RIGHTTOP on_right_handle(handle) on_top_handle(handle) when Shape::Handle::TYPE::RIGHTBOTTOM on_right_handle(handle) on_bottom_handle(handle) when Shape::Handle::TYPE::TOP on_top_handle(handle) when Shape::Handle::TYPE::BOTTOM on_bottom_handle(handle) end end # Draw the shape in the normal way. The function can be overridden if necessary. # @param [Wx::DC] dc Reference to device context where the shape will be drawn to def draw_normal(dc) # HINT: overload it for custom actions... dc.with_pen(border) do dc.with_brush(fill) do dc.draw_rectangle(get_absolute_position.to_point, @rect_size.to_size) end end end # Draw the shape in the hover mode (the mouse cursor is above the shape). # The function can be overridden if necessary. # @param [Wx::DC] dc Reference to device context where the shape will be drawn to def draw_hover(dc) # HINT: overload it for custom actions... dc.with_pen(Wx::Pen.new(hover_colour, 1)) do dc.with_brush(fill) do dc.draw_rectangle(get_absolute_position.to_point, @rect_size.to_size) end end end # Draw the shape in the highlighted mode (another shape is dragged over this # shape and this shape will accept the dragged one if it will be dropped on it). # The function can be overridden if necessary. # @param [Wx::DC] dc Reference to device context where the shape will be drawn to def draw_highlighted(dc) # HINT: overload it for custom actions... dc.with_pen(Wx::Pen.new(hover_colour, 2)) do dc.with_brush(fill) do dc.draw_rectangle(get_absolute_position.to_point, @rect_size.to_size) end end end # Draw shadow under the shape. The function can be overridden if necessary. # @param [Wx::DC] dc Reference to device context where the shadow will be drawn to def draw_shadow(dc) # HINT: overload it for custom actions... if fill.style != Wx::BrushStyle::BRUSHSTYLE_TRANSPARENT dc.with_pen(Wx::TRANSPARENT_PEN) do dc.with_brush(get_parent_canvas.get_shadow_fill) do dc.draw_rectangle((get_absolute_position + get_parent_canvas.get_shadow_offset).to_point, @rect_size.to_size) end end end end # Event handler called during dragging of the right shape handle. # The function can be overridden if necessary. # @param [Shape::Handle] handle Reference to dragged shape handle def on_right_handle(handle) # HINT: overload it for custom actions... @rect_size.x += handle.get_delta.x @rect_size.x = 1.0 if @rect_size.x < 1.0 end # Event handler called during dragging of the left shape handle. # The function can be overridden if necessary. # @param [Shape::Handle] handle Reference to dragged shape handle def on_left_handle(handle) # HINT: overload it for custom actions... dx = handle.get_delta.x.to_f if (@rect_size.x - dx) < 1.0 dx = @rect_size.x - 1.0 end # update position of children unless has_style?(STYLE::LOCK_CHILDREN) @child_shapes.each do |child| child.move_by(-dx, 0) if child.get_h_align == HALIGN::NONE end end # update position and size of the shape @rect_size.x -= dx @relative_position.x += dx end # Event handler called during dragging of the top shape handle. # The function can be overridden if necessary. # @param [Shape::Handle] handle Reference to dragged shape handle def on_top_handle(handle) # HINT: overload it for custom actions... dy = handle.get_delta.y.to_f if (@rect_size.y - dy) < 1.0 dy = @rect_size.y - 1.0 end # update position of children unless has_style?(STYLE::LOCK_CHILDREN) @child_shapes.each do |child| child.move_by(0, -dy) if child.get_v_align == VALIGN::NONE end end # update position and size of the shape @rect_size.y -= dy @relative_position.y += dy end # Event handler called during dragging of the bottom shape handle. # The function can be overridden if necessary. # @param [Shape::Handle] handle Reference to dragged shape handle def on_bottom_handle(handle) # HINT: overload it for custom actions... @rect_size.y += handle.get_delta.y @rect_size.y = 1.0 if @rect_size.y < 1.0 end def serialize_rect_size(*val) @rect_size = val.first unless val.empty? @rect_size end private :serialize_rect_size def serialize_rect_fill(*val) @fill = val.first unless val.empty? @fill end private :serialize_rect_fill def serialize_rect_border(*val) @border = val.first unless val.empty? @border end private :serialize_rect_border end end