samples/demo/frame_canvas.rb in wxruby3-shapes-0.9.0.pre.beta.3 vs samples/demo/frame_canvas.rb in wxruby3-shapes-0.9.5

- old
+ new

@@ -1,12 +1,54 @@ # Wx::SF - Demo FrameCanvas # Copyright (c) M.J.N. Corino, The Netherlands -require 'wx/shapes' +require_relative './dialogs' class FrameCanvas < Wx::SF::ShapeCanvas + module POPUP_ID + include Wx::IDHelper + + # commmon + STYLE = self.next_id + HOVER_COLOR = self.next_id + HALIGN = self.next_id + VALIGN = self.next_id + HBORDER = self.next_id + VBORDER = self.next_id + ACCEPTED = self.next_id + ACC_CHILDREN = self.next_id + ACC_CONNECTIONS = self.next_id + ACC_CONNECTION_FROM = self.next_id + ACC_CONNECTION_TO = self.next_id + CONNECTION_POINTS = self.next_id + + # rect + FILL_BRUSH = self.next_id + BORDER_PEN = self.next_id + + # line + LINE_PEN = self.next_id + LINE_ARROWS = self.next_id + SRC_ARROW = self.next_id + TRG_ARROW = self.next_id + + # text + TEXT_FONT = self.next_id + TEXT_COLOR = self.next_id + + # box + BOX_SPACING = self.next_id + + # grid + GRID_SETTINGS = self.next_id + GRID_SPACING = self.next_id + GRID_MAXROWS = self.next_id + + DUMP = self.next_id + end + # Constructor # @param [Wx::SF::Diagram] diagram shape diagram # @param [Wx::Window] parent Parent window # @param [Integer] id Window ID def initialize(diagram, parent, id = Wx::ID_ANY) @@ -31,12 +73,12 @@ # the canvas background can be filled with a solid colour ... # remove_style(STYLE::GRADIENT_BACKGROUND) # set_background_colour(DEFAULT::SHAPECANVAS_BACKGROUNDCOLOR) # ... or by a gradient fill add_style(STYLE::GRADIENT_BACKGROUND) - set_gradient_from(DEFAULT::GRADIENT_FROM) - set_gradient_to(DEFAULT::GRADIENT_TO) + set_gradient_from(DEFAULT.gradient_from) + set_gradient_to(DEFAULT.gradient_to) # also shadows style can be set here: # set_shadow_fill(Wx::Brush.new(Wx::Colour.new(100, 100, 100), Wx::CROSSDIAG_HATCH)) # standard values can be DEFAULT::SHAPECANVAS_SHADOWBRUSH or DEFAULT::SHAPECANVAS_SHADOWCOLOR # set_shadow_offset(Wx::RealPoint.new(7, 7)) @@ -159,10 +201,12 @@ _, shape = get_diagram.create_shape(Wx::SF::RoundRectShape, event.get_position, Wx::SF::DONT_SAVE_STATE) if shape # set shape policy shape.accept_child(Wx::SF::TextShape) shape.accept_child(Wx::SF::EditTextShape) + shape.accept_child(Wx::SF::RectShape) + shape.accept_child(Wx::SF::VBoxShape) shape.accept_connection(Wx::SF::ACCEPT_ALL) shape.accept_src_neighbour(Wx::SF::ACCEPT_ALL) shape.accept_trg_neighbour(Wx::SF::ACCEPT_ALL) end @@ -208,12 +252,12 @@ if shape # set visual style shape.set_fill(Wx::TRANSPARENT_BRUSH) shape.set_border(Wx::Pen.new(Wx::BLACK, 1, Wx::PenStyle::PENSTYLE_DOT)) - # number of rows and columns cas be set here (default grid dimension is 3x3) ... - # shape.set_dimensions(2, 2) + # number of rows and columns cas be set here (default grid dimension is 3x1) ... + shape.set_dimensions(2, @parent_frame.grid_columns) # ... as well as the cell spaces (default grid cellspace is 5). # shape.set_cell_space(0) # set shape policy @@ -250,10 +294,57 @@ # update the grid shape.update end + when MainFrame::MODE::VBOX, MainFrame::MODE::HBOX + if @parent_frame.tool_mode == MainFrame::MODE::VBOX + _, shape = get_diagram.create_shape(Wx::SF::VBoxShape, event.get_position, Wx::SF::DONT_SAVE_STATE) + else + _, shape = get_diagram.create_shape(Wx::SF::HBoxShape, event.get_position, Wx::SF::DONT_SAVE_STATE) + end + + if shape + # set visual style + shape.set_fill(Wx::TRANSPARENT_BRUSH) + + # spacing can be set here (default spacing is 3). + # shape.set_spacing(0) + + # set shape policy + shape.accept_child(Wx::SF::ACCEPT_ALL) + + shape.accept_connection(Wx::SF::ACCEPT_ALL) + shape.accept_src_neighbour(Wx::SF::ACCEPT_ALL) + shape.accept_trg_neighbour(Wx::SF::ACCEPT_ALL) + + if @parent_frame.tool_mode == MainFrame::MODE::VBOX + + else + # insert some shapes into the grid from code here (it can also be done interactively by drag&drop operations). + # shapes inserted to the grid can be aligned relatively to its grid cell region + _, inner_shape = get_diagram.create_shape(Wx::SF::EllipseShape, Wx::SF::DONT_SAVE_STATE) + inner_shape.set_v_align(Wx::SF::Shape::VALIGN::EXPAND ) + shape.append_to_box(inner_shape) + # add another shape... + _, inner_shape = get_diagram.create_shape(Wx::SF::DiamondShape, Wx::SF::DONT_SAVE_STATE) + shape.append_to_box(inner_shape) + end + + # also control shapes can be managed by the grid shape. + # _, ctrl = get_diagram.create_shape(Wx::SF::ControlShape, event.get_position, Wx::SF::DONT_SAVE_STATE) + # if ctrl ) + # ctrl.set_v_align(Wx::SF::Shape::VALIGN::EXPAND) + # ctrl.set_h_align(Wx::SF::Shape::HALIGN::EXPAND) + # ctrl.set_control(Wx::Button.new( self, Wx::ID_ANY, "Test")) + # shape.append_to_box(ctrl) + # end + + # update the box + shape.update + end + when MainFrame::MODE::ELLIPSE _, shape = get_diagram.create_shape(Wx::SF::EllipseShape, event.get_position, Wx::SF::DONT_SAVE_STATE) if shape # set shape policy shape.accept_child(Wx::SF::TextShape) @@ -309,18 +400,15 @@ else super end when MainFrame::MODE::STANDALONELINE - _, shape = get_diagram.create_shape(Wx::SF::LineShape, event.get_position, Wx::SF::DONT_SAVE_STATE) + _, shape = get_diagram.insert_shape(Wx::SF::LineShape.new((event.get_position - [50, 0]).to_real, + (event.get_position + [50, 0]).to_real), + event.get_position, + Wx::SF::DONT_SAVE_STATE) if shape - # set the line to be stand-alone - shape.set_stand_alone(true) - - shape.set_src_point((event.get_position - [50, 0]).to_real_point) - shape.set_trg_point((event.get_position + [50, 0]).to_real_point) - # line's ending style can be set as follows: # shape.set_src_arrow(Wx::SF::CircleArrow) # shape.set_trg_arrow(Wx::SF::CircleArrow) shape.accept_child(Wx::SF::TextShape) @@ -341,40 +429,328 @@ shape.refresh end end + def create_shape_popup + menu = Wx::Menu.new + menu.append(POPUP_ID::STYLE, 'Change style', 'Change style') + menu.append(POPUP_ID::HOVER_COLOR, 'Change hover colour', 'Change hover colour') + menu.append(POPUP_ID::HALIGN, 'Change horizontal alignment', 'Change horizontal alignment') + menu.append(POPUP_ID::VALIGN, 'Change vertical alignment', 'Change vertical alignment') + menu.append(POPUP_ID::HBORDER, 'Change horizontal margin', 'Change horizontal margin') + menu.append(POPUP_ID::VBORDER, 'Change vertical margin', 'Change vertical margin') + submenu = Wx::Menu.new + submenu.append(POPUP_ID::ACC_CHILDREN, 'Child shapes', 'Change accepted child shapes') + submenu.append(POPUP_ID::ACC_CONNECTIONS, 'Connections', 'Change accepted connections') + submenu.append(POPUP_ID::ACC_CONNECTION_FROM, 'Connection sources', 'Change accepted connection sources') + submenu.append(POPUP_ID::ACC_CONNECTION_TO, 'Connection targets', 'Change accepted connection targets') + menu.append(Wx::MenuItem.new(submenu, POPUP_ID::ACCEPTED, 'Change accepted', '', Wx::ItemKind::ITEM_NORMAL, submenu)) + menu.append(POPUP_ID::CONNECTION_POINTS, 'Change connection points', 'Change connection points') + + @rect_mi = [] + @rect_mi << Wx::MenuItem.new(menu, POPUP_ID::FILL_BRUSH, 'Change fill', 'Change fill brush') + @rect_mi << Wx::MenuItem.new(menu, POPUP_ID::BORDER_PEN, 'Change border', 'Change border pen') + + @line_mi = [] + @line_mi << Wx::MenuItem.new(menu, POPUP_ID::LINE_PEN, 'Change line', 'Change line pen') + submenu = Wx::Menu.new + submenu.append(POPUP_ID::SRC_ARROW, 'Change source arrow', 'Change source arrow') + submenu.append(POPUP_ID::TRG_ARROW, 'Change target arrow', 'Change target arrow') + @line_mi << Wx::MenuItem.new(menu, POPUP_ID::LINE_ARROWS, 'Change arrows', '', Wx::ItemKind::ITEM_NORMAL, submenu) + + @text_mi = [] + @text_mi << Wx::MenuItem.new(menu, POPUP_ID::TEXT_FONT, 'Change text font', 'Change text font') + @text_mi << Wx::MenuItem.new(menu, POPUP_ID::TEXT_COLOR, 'Change text colour', 'Change text colour') + + @box_mi = Wx::MenuItem.new(menu, POPUP_ID::BOX_SPACING, 'Change slot spacing', 'Change slot spacing') + + submenu = Wx::Menu.new + submenu.append(POPUP_ID::GRID_SPACING, 'Change cell spacing', 'Change cell spacing') + submenu.append(POPUP_ID::GRID_MAXROWS, 'Change row maximum', 'Change row maximum') + @grid_mi = Wx::MenuItem.new(menu, POPUP_ID::GRID_SETTINGS, 'Change grid settings', 'Change grid settings', Wx::ItemKind::ITEM_NORMAL, submenu) + + menu.append_separator + menu.append(POPUP_ID::DUMP, 'Show serialized state', 'Show serialized state') + menu + end + private :create_shape_popup + + def remove_popup_items(*item_ids) + item_ids.flatten.each { |id| @popup.remove(id) if @popup.find_item(id) } + end + private :remove_popup_items + + def get_shape_popup(shape) + @popup ||= create_shape_popup + case shape + when Wx::SF::RectShape + remove_popup_items(POPUP_ID::LINE_PEN, POPUP_ID::LINE_ARROWS) + n = @popup.get_menu_item_count-2 + @rect_mi.reverse.each { |mi| @popup.insert(n, mi) unless @popup.find_item(mi.id) } + case shape + when Wx::SF::TextShape + remove_popup_items(POPUP_ID::BOX_SPACING, POPUP_ID::GRID_SETTINGS) + n = @popup.get_menu_item_count-2 + @text_mi.reverse.each { |mi| @popup.insert(n, mi) unless @popup.find_item(mi.id) } + when Wx::SF::BoxShape + remove_popup_items(POPUP_ID::TEXT_FONT, POPUP_ID::TEXT_COLOR, POPUP_ID::GRID_SETTINGS) + n = @popup.get_menu_item_count-2 + @popup.insert(n, @box_mi) unless @popup.find_item(@box_mi.id) + when Wx::SF::GridShape + remove_popup_items(POPUP_ID::TEXT_FONT, POPUP_ID::TEXT_COLOR, POPUP_ID::BOX_SPACING) + n = @popup.get_menu_item_count-2 + @popup.insert(n, @grid_mi) unless @popup.find_item(@grid_mi.id) + else + remove_popup_items(POPUP_ID::TEXT_FONT, POPUP_ID::TEXT_COLOR, POPUP_ID::BOX_SPACING, POPUP_ID::GRID_SETTINGS) + end + when Wx::SF::LineShape + remove_popup_items(POPUP_ID::FILL_BRUSH, POPUP_ID::BORDER_PEN, + POPUP_ID::TEXT_FONT, POPUP_ID::TEXT_COLOR, + POPUP_ID::BOX_SPACING, POPUP_ID::GRID_SETTINGS) + n = @popup.get_menu_item_count-2 + @line_mi.reverse.each { |mi| @popup.insert(n, mi) unless @popup.find_item(mi.id) } + else + remove_popup_items(POPUP_ID::FILL_BRUSH, POPUP_ID::BORDER_PEN, + POPUP_ID::LINE_PEN, POPUP_ID::LINE_ARROWS, + POPUP_ID::TEXT_FONT, POPUP_ID::TEXT_COLOR, + POPUP_ID::BOX_SPACING, POPUP_ID::GRID_SETTINGS) + end + @popup + end + private :get_shape_popup + + def get_enum_choices(enum, excludes: nil) + Dialogs.get_enum_choices(enum, excludes: excludes) + end + + def get_enum_index(enumerator, excludes: nil) + Dialogs.get_enum_index(enumerator, excludes: excludes) + end + + def index_to_enum(enum, index, excludes: nil) + Dialogs.index_to_enum(enum, index, excludes: excludes) + end + + def selections_to_enum(enum, selections, excludes: nil) + Dialogs.selections_to_enum(enum, selections, excludes: excludes) + end + + def enum_to_selections(enum, style, excludes: nil) + Dialogs.enum_to_selections(enum, style, excludes: excludes) + end + + SHAPES = [ + Wx::SF::RectShape, + Wx::SF::BitmapShape, + Wx::SF::SquareShape, + Wx::SF::CircleShape, + Wx::SF::PolygonShape, + Wx::SF::TextShape, + Wx::SF::RoundRectShape, + Wx::SF::GridShape, + Wx::SF::FlexGridShape, + Wx::SF::EllipseShape, + Wx::SF::ControlShape, + Wx::SF::BoxShape, + Wx::SF::VBoxShape, + Wx::SF::HBoxShape, + Wx::SF::DiamondShape, + Wx::SF::EditTextShape + ] + + CONNECTION_SHAPES = [ + Wx::SF::LineShape, + Wx::SF::CurveShape, + Wx::SF::OrthoLineShape, + Wx::SF::RoundOrthoLineShape + ] + def on_right_down(event) # try to find shape under cursor shape = get_shape_under_cursor # alternatively you can use: # shape = get_shape_at_position(dp2lp(event.getposition), 1, SEARCHMODE::BOTH) - # print out information about the shape (if found) + # show shape popup (if found) if shape - # show basic info - msg = "Class name: #{shape.class}, ID: #{shape.get_id}\n" - - # show info about shape's children - lst_shapes = shape.get_child_shapes(nil, Wx::SF::RECURSIVE) - unless lst_shapes.empty? - msg << "\nChildren:\n" - lst_shapes.each_with_index do |child, i| - msg << "#{i+1}. Class name: #{child.class}, ID: #{child.get_id}\n" + case self.get_popup_menu_selection_from_user(get_shape_popup(shape)) + when POPUP_ID::STYLE + choices = get_enum_choices(Wx::SF::Shape::STYLE, + excludes: %i[DEFAULT_SHAPE_STYLE PROPAGATE_ALL]) + sel = Wx.get_selected_choices('Select styles', + 'Select multiple', + choices, + self, + initial_selections: enum_to_selections(Wx::SF::Shape::STYLE, shape.get_style, + excludes: %i[DEFAULT_SHAPE_STYLE PROPAGATE_ALL])) + if sel + shape.set_style(selections_to_enum(Wx::SF::Shape::STYLE, sel, + excludes: %i[DEFAULT_SHAPE_STYLE PROPAGATE_ALL])) + shape.update end - end - - # show info about shape's neighbours - lst_shapes = shape.get_neighbours(Wx::SF::LineShape, Wx::SF::Shape::CONNECTMODE::BOTH, Wx::SF::INDIRECT) - unless lst_shapes.empty? - msg << "\nNeighbours:\n" - lst_shapes.each_with_index do |nbr, i| - msg << "#{i+1}. Class name: #{nbr.class}, ID: #{nbr.get_id}\n" + when POPUP_ID::HOVER_COLOR + color = Wx.get_colour_from_user(self, shape.get_hover_colour, 'Select hover colour') + if color.ok? + shape.set_hover_colour(color) + shape.update end + when POPUP_ID::HALIGN + case Wx.get_single_choice('Select horizontal alignment', + 'Select', + %w[NONE LEFT CENTER RIGHT EXPAND], + self, + initial_selection: shape.get_h_align.to_i) + when 'NONE' then shape.set_h_align(Wx::SF::Shape::HALIGN::NONE) + when 'LEFT' then shape.set_h_align(Wx::SF::Shape::HALIGN::LEFT) + when 'CENTER' then shape.set_h_align(Wx::SF::Shape::HALIGN::CENTER) + when 'RIGHT' then shape.set_h_align(Wx::SF::Shape::HALIGN::RIGHT) + when 'EXPAND' then shape.set_h_align(Wx::SF::Shape::HALIGN::EXPAND) + end + shape.update + when POPUP_ID::VALIGN + case Wx.get_single_choice('Select vertical alignment', + 'Select', + %w[NONE TOP MIDDLE BOTTOM EXPAND], + self, + initial_selection: shape.get_v_align.to_i) + when 'NONE' then shape.set_v_align(Wx::SF::Shape::VALIGN::NONE) + when 'TOP' then shape.set_v_align(Wx::SF::Shape::VALIGN::TOP) + when 'MIDDLE' then shape.set_v_align(Wx::SF::Shape::VALIGN::MIDDLE) + when 'BOTTOM' then shape.set_v_align(Wx::SF::Shape::VALIGN::BOTTOM) + when 'EXPAND' then shape.set_v_align(Wx::SF::Shape::HALIGN::EXPAND) + end + shape.update + when POPUP_ID::HBORDER + Dialogs.FloatDialog(self, 'Enter horizontal margin', value: shape.get_h_border) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.set_h_border(dlg.get_value) + shape.update + end + end + when POPUP_ID::VBORDER + Dialogs.FloatDialog(self, 'Enter vertical margin', value: shape.get_v_border) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.set_v_border(dlg.get_value) + shape.update + end + end + when POPUP_ID::ACC_CHILDREN + Dialogs.AcceptedShapesDialog(self, 'Select acceptable child shapes.', SHAPES, shape.accepted_children) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.accepted_children.clear + if (ss = dlg.get_selected_shapes(SHAPES)) + shape.accepted_children.merge(ss) + end + end + end + when POPUP_ID::ACC_CONNECTIONS + Dialogs.AcceptedShapesDialog(self, 'Select acceptable connection shapes.', CONNECTION_SHAPES, shape.accepted_connections) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.accepted_connections.clear + if (ss = dlg.get_selected_shapes(CONNECTION_SHAPES)) + shape.accepted_connections.merge(ss) + end + end + end + when POPUP_ID::ACC_CONNECTION_FROM + shape_options = SHAPES - [shape.class] + Dialogs.AcceptedShapesDialog(self, 'Select acceptable connection source shapes.', shape_options, shape.accepted_src_neighbours) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.accepted_src_neighbours.clear + if (ss = dlg.get_selected_shapes(shape_options)) + shape.accepted_src_neighbours.merge(ss) + end + end + end + when POPUP_ID::ACC_CONNECTION_TO + shape_options = SHAPES - [shape.class] + Dialogs.AcceptedShapesDialog(self, 'Select acceptable connection target shapes.', shape_options, shape.accepted_trg_neighbours) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.accepted_trg_neighbours.clear + if (ss = dlg.get_selected_shapes(shape_options)) + shape.accepted_trg_neighbours.merge(ss) + end + end + end + when POPUP_ID::CONNECTION_POINTS + Dialogs.ConnectionPointDialog(self, shape.connection_points) do |dlg| + if dlg.show_modal == Wx::ID_OK + dlg.set_shape_connection_points(shape) + shape.update + end + end + when POPUP_ID::FILL_BRUSH + Dialogs.BrushDialog(self, 'Fill brush', shape.fill) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.set_fill(dlg.get_brush) + shape.update + end + end + when POPUP_ID::BORDER_PEN + Dialogs.PenDialog(self, 'Border pen', shape.border) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.set_border(dlg.get_pen) + shape.update + end + end + when POPUP_ID::TEXT_FONT + new_font = Wx.get_font_from_user(self, shape.font, 'Select text font') + if new_font.ok? + shape.font = new_font + shape.update + end + when POPUP_ID::TEXT_COLOR + color = Wx.get_colour_from_user(self, shape.text_colour, 'Select text colour') + if color.ok? + shape.text_colour(color) + shape.update + end + when POPUP_ID::BOX_SPACING + spc = Wx.get_number_from_user('Enter BoxShape slot spacing.', 'Value:', 'Slot spacing', + shape.spacing, 0, 100, self) + if spc >= 0 + shape.spacing = spc + shape.update + end + when POPUP_ID::GRID_SPACING + spc = Wx.get_number_from_user('Enter GridShape cell spacing.', 'Value:', 'Cell spacing', + shape.cell_space, 0, 100, self) + if spc >= 0 + shape.cell_space = spc + shape.update + end + when POPUP_ID::GRID_MAXROWS + spc = Wx.get_number_from_user('Enter GridShape maximum rows.', 'Value:', 'Maximum rows', + shape.max_rows, 0, 100, self) + if spc >= 0 + shape.max_rows = spc + shape.update + end + when POPUP_ID::LINE_PEN + Dialogs.PenDialog(self, 'Line pen', shape.line_pen) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.set_line_pen(dlg.get_pen) + shape.update + end + end + when POPUP_ID::SRC_ARROW + Dialogs.ArrowDialog(self, 'Source arrow', shape.get_src_arrow) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.src_arrow = dlg.get_arrow + shape.update + end + end + when POPUP_ID::TRG_ARROW + Dialogs.ArrowDialog(self, 'Target arrow', shape.get_trg_arrow) do |dlg| + if dlg.show_modal == Wx::ID_OK + shape.trg_arrow = dlg.get_arrow + shape.update + end + end + when POPUP_ID::DUMP + Dialogs.StateDialog(self, shape) end - - # show message - Wx.message_box(msg, 'wxRuby ShapeFramework', Wx::OK | Wx::ICON_INFORMATION) else Wx.message_box('No shape found on this position.', 'wxRuby ShapeFramework', Wx::OK | Wx::ICON_INFORMATION) end # call default handler