Class: Wx::SF::GridShape

Inherits:
RectShape show all
Includes:
ManagerShape
Defined in:
lib/wx/shapes/shapes/grid_shape.rb

Overview

Class encapsulates a rectangular shape derived from Wx::SF::RectShape class which acts as a grid-based container able to manage other assigned child shapes (it can control their position). The managed shapes are aligned into defined grid with a behaviour similar to classic Wx::GridSizer class.

Direct Known Subclasses

FlexGridShape

Defined Under Namespace

Modules: DEFAULT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ManagerShape

#do_alignment, #fit_shape_to_rect, #is_manager

Methods inherited from RectShape

#create_handles, #do_begin_handle, #do_on_handle, #draw_highlighted, #draw_hover, #draw_normal, #draw_shadow, #get_border, #get_border_point, #get_bounding_box, #get_fill, #get_rect_size, #on_begin_handle, #on_bottom_handle, #on_handle, #on_left_handle, #on_right_handle, #on_top_handle, #scale, #scale_rectangle, #set_border, #set_fill, #set_rect_size

Methods inherited from Shape

#accept_child, #accept_connection, #accept_currently_dragged_shapes, #accept_src_neighbour, #accept_trg_neighbour, #activate, #active?, #add_child_shape, #add_connection_point, #add_handle, #add_style, #ancestor?, #clear_accepted_childs, #clear_accepted_connections, #clear_accepted_src_neighbours, #clear_accepted_trg_neighbours, component, component_shapes, #contains?, #contains_style, #create_handles, #descendant?, #do_alignment, #does_not_accept_children?, #draw, #draw_highlighted, #draw_hover, #draw_normal, #draw_selected, #draw_shadow, #get_absolute_position, #get_accepted_children, #get_accepted_connections, #get_accepted_src_neighbours, #get_accepted_trg_neighbours, #get_assigned_connections, #get_border_point, #get_bounding_box, #get_center, #get_child_shapes, #get_children, #get_children_recursively, #get_complete_bounding_box, #get_connection_point, #get_connection_points, #get_custom_dock_point, #get_diagram, #get_grand_parent_shape, #get_h_align, #get_h_border, #get_handle, #get_handles, #get_hover_colour, #get_nearest_connection_point, #get_neighbours, #get_parent_absolute_position, #get_parent_canvas, #get_parent_shape, #get_relative_position, #get_shape_canvas, #get_style, #get_user_data, #get_v_align, #get_v_border, #has_children, #has_selected_parent?, #include_child_shape?, #inside?, #inspect, #intersects?, #is_child_accepted, #is_connection_accepted, #is_managed, #is_manager, #is_src_neighbour_accepted, #is_trg_neighbour_accepted, lines_intersection, #lines_intersection, #move_by, #move_to, #on_begin_drag, #on_begin_handle, #on_dragging, #on_end_drag, #on_end_handle, #on_handle, #on_key, #on_left_click, #on_left_double_click, #on_mouse_enter, #on_mouse_leave, #on_mouse_over, #on_right_click, #on_right_double_click, #refresh, #refresh_rect, #remove_connection_point, #remove_handle, #remove_style, #scale, #scale_children, #select, #selected?, #set_custom_dock_point, #set_diagram, #set_h_align, #set_h_border, #set_hover_colour, #set_parent_shape, #set_relative_position, #set_style, #set_user_data, #set_v_align, #set_v_border, #show, #show_handles, #to_s, #visible?

Constructor Details

#initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE, cols: DEFAULT::COLUMNS, max_rows: 0, cell_space: DEFAULT::CELLSPACE, diagram: nil) ⇒ GridShape

Constructor.

Parameters:

  • pos (Wx::RealPoint, Wx::Point) (defaults to: Shape::DEFAULT::POSITION)

    Initial position

  • size (Wx::RealPoint, Wx::Size, Wx::Point) (defaults to: RectShape::DEFAULT::SIZE)

    Initial size

  • cols (Integer) (defaults to: DEFAULT::COLUMNS)

    Number of grid columns

  • max_rows (Integer) (defaults to: 0)

    Maximum number of grid rows

  • cell_space (Integer) (defaults to: DEFAULT::CELLSPACE)

    Additional space between managed shapes

  • diagram (Wx::SF::Diagram) (defaults to: nil)

    parent diagram



60
61
62
63
64
65
66
67
68
69
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 60

def initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE,
               cols: DEFAULT::COLUMNS, max_rows: 0, cell_space: DEFAULT::CELLSPACE, diagram: nil)
  super(pos, size, diagram: diagram)
  @cols = [1, cols.to_i].max   # at least one column
  @max_rows = [0, max_rows.to_i].max              # no or >=1 max rows
  @cell_space = [0, cell_space.to_i].max
  @rows = 1
  @cells = []
  remove_style(Shape::STYLE::SIZE_CHANGE)
end

Instance Attribute Details

#max_rowsObject (readonly)

Returns the value of attribute max_rows.



71
72
73
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 71

def max_rows
  @max_rows
end

Class Method Details

.get_min_sizeWx::Size Also known as: min_size

Returns the minimum size for empty grids

Returns:



28
29
30
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 28

def get_min_size
  @min_size ||= Wx::Size.new(20, 20)
end

.set_min_size(sz) ⇒ Object .set_min_size(w, h) ⇒ Object Also known as: min_size=

Sets the minimum size for empty grids

Overloads:

  • .set_min_size(sz) ⇒ Object

    Parameters:

  • .set_min_size(w, h) ⇒ Object

    Parameters:

    • w (Integer)
    • h (Integer)


39
40
41
42
43
44
45
46
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 39

def set_min_size(arg1, arg2 = nil)
  @min_size = if arg2.nil?
                raise ArgumentError, 'Expected Wx::Size' unless Wx::Size === arg1
                arg1
              else
                Wx::Size.new(arg1, arg2)
              end
end

Instance Method Details

#append_to_grid(shape) ⇒ Object

Append given shape to the grid at the last managed position.

Parameters:

  • shape (Shape)

    shape to append



188
189
190
191
192
193
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 188

def append_to_grid(shape)
  row = @cells.size / @cols
  col = @cells.size - row*@cols

  insert_to_grid(row, col, shape)
end

#clear_cell(row, col) ⇒ Boolean

Clear the cell at given row and column index Note that this function doesn’t remove managed (child) shapes from the parent grid shape (they are still its child shapes but aren’t managed anymore).

Parameters:

  • row (Integer)
  • col (Integer)

Returns:

  • (Boolean)

    true if cell existed, false otherwise



155
156
157
158
159
160
161
162
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 155

def clear_cell(row, col)
  if row>=0 && row<@rows && col>=0 && col<@cols
    @cells[row*@cols + col] = nil
    true
  else
    false
  end
end

#clear_gridObject

Clear information about managed shapes and set number of rows to 1 (number of columns does not change).

Note that this function doesn’t remove managed (child) shapes from the parent grid shape (they are still its child shapes but aren’t managed anymore).



181
182
183
184
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 181

def clear_grid
  @rows = 1
  @cells = []
end

#do_children_layoutObject (protected)

Do layout of assigned child shapes



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 366

def do_children_layout
  return if @cols == 0 || @rows == 0

  max_size = get_max_child_size

  @cells.each_with_index do |shape, i|
    if shape
      col = (i % @cols)
      row = (i / @cols)

      fit_shape_to_rect(shape, Wx::Rect.new(col*max_size.width + (col+1)*@cell_space,
                                            row*max_size.height + (row+1)*@cell_space,
                                            max_size.width, max_size.height))
    end
  end
end

#each_cellEnumerator #each_cell {|row, col, shape| ... } ⇒ Object

Iterate all cells. If a block is given passes row, col and shape (if any) for each cell to block. Returns Enumerator if no block given.

Overloads:

  • #each_cellEnumerator

    Returns:

    • (Enumerator)
  • #each_cell {|row, col, shape| ... } ⇒ Object

    Yield Parameters:

    • row (Integer)
    • col (Integer)
    • shape (shape, nil)

    Returns:

    • (Object)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 131

def each_cell(&block)
  if block
    @rows.times do |row|
      @cols.times do |col|
        block.call(row, col, @cells[row*@cols + col])
      end
    end
  else
    ::Enumerator.new do |y|
      @rows.times do |row|
        @cols.times do |col|
          y << [row, col, @cells[row*@cols + col]]
        end
      end
    end
  end
end

#find_cell(child_rect) ⇒ Object (protected)



412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 412

def find_cell(child_rect)
  max_size = get_max_child_size
  child_centre = child_rect.get_position
  child_centre.x += child_rect.width/2
  child_centre.y += child_rect.height/2
  # find the cell index where the new or dragged child is positioned above and in front of
  offset = get_bounding_box.top_left
  cell_count.times.find do |cell|
    col = (cell % @cols)
    row = (cell / @cols)
    cell_rct = Wx::Rect.new(col*max_size.width + (col+1)*@cell_space,
                            row*max_size.height + (row+1)*@cell_space,
                            max_size.width, max_size.height).offset!(offset)
    child_centre.x <= cell_rct.right && child_centre.y <= cell_rct.bottom
  end
end

#fit_to_childrenObject

Resize the shape to bound all child shapes. The function can be overridden if necessary.



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 331

def fit_to_children
  # get bounding box of the shape and children set to be inside it
  abs_pos = get_absolute_position
  ch_bb = Wx::Rect.new(abs_pos.to_point, [0, 0])
  
  @child_shapes.each do |child|
    ch_bb = child.get_complete_bounding_box(ch_bb, BBMODE::SELF | BBMODE::CHILDREN) if child.has_style?(STYLE::ALWAYS_INSIDE)
  end

  if @child_shapes.empty?
    # do not let the empty grid shape 'disappear' due to zero sizes...
    ch_bb.size = GridShape.min_size - @cell_space
  end

  @rect_size = Wx::RealPoint.new(ch_bb.width + @cell_space, ch_bb.height + @cell_space)
end

#get_cell_countInteger Also known as: cell_count

Get number of available grid cells

Returns:

  • (Integer)


103
104
105
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 103

def get_cell_count
  @rows * @cols
end

#get_cell_spaceInteger Also known as: cell_space

Get space between grid cells (managed shapes).

Returns:

  • (Integer)

    Cellspace size



117
118
119
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 117

def get_cell_space
  @cell_space
end

#get_dimensionsArray(Integer,Integer)

Get grid dimensions.

Returns:

  • (Array(Integer,Integer))

    row and col numbers



97
98
99
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 97

def get_dimensions
  [@rows, @cols]
end

#get_managed_shape(index) ⇒ Shape? #get_managed_shape(row, col) ⇒ Shape?

Get managed shape specified by lexicographic cell index.

Overloads:

  • #get_managed_shape(index) ⇒ Shape?

    Returns shape object of given cell index if exists, otherwise nil.

    Parameters:

    • index (Integer)

      Lexicographic index of requested shape

    Returns:

    • (Shape, nil)

      shape object of given cell index if exists, otherwise nil

  • #get_managed_shape(row, col) ⇒ Shape?

    Returns shape object stored in specified grid cell if exists, otherwise nil.

    Parameters:

    • row (Integer)

      Zero-base row index

    • col (Integer)

      Zero-based column index

    Returns:

    • (Shape, nil)

      shape object stored in specified grid cell if exists, otherwise nil



172
173
174
175
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 172

def get_managed_shape(*args)
  index = args.size == 1 ? args.first : (args[0]*@cols)+args[1]
  @cells[index]
end

#get_max_child_sizeWx::Size (protected)

returns maximum size of all managed (child) shapes

Returns:



402
403
404
405
406
407
408
409
410
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 402

def get_max_child_size
  @child_shapes.inject(Wx::Size.new(0, 0)) do |max_size, shape|
    child_rect = shape.get_bounding_box

    max_size.set_width(child_rect.width) if shape.get_h_align != HALIGN::EXPAND && child_rect.width > max_size.width
    max_size.set_height(child_rect.height) if shape.get_v_align != VALIGN::EXPAND && child_rect.height > max_size.height
    max_size
  end
end

#insert_to_grid(row, col, shape) ⇒ Boolean #insert_to_grid(index, shape) ⇒ Boolean

Insert given shape to the grid at the given position. In case a shape is inserted in a cell already occupied the cells at that position and following will be shifted to the next lexicographic position. A maximum row setting may prevent a new shape of being inserted.

Overloads:

  • #insert_to_grid(row, col, shape) ⇒ Boolean

    Note that the grid can grow in a vertical direction only, so if the user specifies a desired horizontal position bigger than the current number of columns is then this function exits with an error (false) return value. If specified vertical position exceeds the number or grid rows than the grid is resized.

    Parameters:

    • row (Integer)

      Vertical position

    • col (Integer)

      Horizontal position

    • shape (Shape)

      shape to insert

    Returns:

    • (Boolean)

      true on success, otherwise false

  • #insert_to_grid(index, shape) ⇒ Boolean

    Note that the given index is a lexicographic position of inserted shape. The given shape is inserted before the existing item ‘index’, thus insert_to_grid(0, something) will insert an item in such way that it will become the first grid element.

    Parameters:

    • index (Integer)

      Lexicographic position of inserted shape (>= 0)

    • shape (Shape)

      shape to insert

    Returns:

    • (Boolean)

      true on success, otherwise false



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 215

def insert_to_grid(*args)
  if args.size > 2
    row, col, shape = args
    if shape && shape.is_a?(Shape) && is_child_accepted(shape.class)
      # protect duplicated occurrences
      return false if @cells.index(shape)

      # protect unbounded horizontal index (grid can grow in a vertical direction only)
      return false if col >= @cols
      # protect maximum rows
      index = row * @cols + col
      return false if @max_rows > 0 &&
        (row >= @max_rows ||                                      # cannot insert beyond max_rows
          (@cells[index] && @cells.size >= (@max_rows * @cols)))  # cannot grow beyond max_rows

      # add the shape to the children list if necessary
      unless @child_shapes.include?(shape)
        if @diagram
          @diagram.reparent_shape(shape, self)
        else
          shape.set_parent_shape(self)
        end
      end

      if @cells[index]
        @cells.insert(row * @cols + col, shape)
      else
        @cells[index] = shape
      end

      # adjust row count
      update_rows

      return true
    end
  else
    index, shape = args
    if shape && shape.is_a?(Shape) && is_child_accepted(shape.class)
      # protect duplicated occurrences
      return false if @cells.index(shape)

      # protect unbounded index
      max_size = @cols * @max_rows
      return false if index < 0 ||
        (@max_rows > 0 &&
          (index >= max_size ||                                       # cannot insert beyond max_rows
            (@cells[index] && @cells.size >= (@cols * @max_rows))))   # cannot grow beyond max_rows

      # add the shape to the children list if necessary
      unless @child_shapes.include?(shape)
        if @diagram
          @diagram.reparent_shape(shape, self)
        else
          shape.set_parent_shape(self)
        end
      end

      if @cells[index]
        @cells.insert(index, shape)
      else
        @cells[index] = shape
      end

      # adjust row count
      update_rows

      return true
    end
  end
  false
end

#on_child_dropped(_pos, child) ⇒ Object

Event handler called when any shape is dropped above this shape (and the dropped shape is accepted as a child of this shape). The function can be overridden if necessary.

The function is called by the framework (by the shape canvas).

Parameters:

  • _pos (Wx::RealPoint)

    Relative position of dropped shape

  • child (Shape)

    dropped shape



354
355
356
357
358
359
360
361
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 354

def on_child_dropped(_pos, child)
  # see if we can match the position of the new child with the position of another
  # (previously assigned) managed shape
  if child && !child.is_a?(LineShape)
    # insert child based on it's current (possibly dropped) position
    position_child_cell(child)
  end
end

#on_importObject (protected)

called after the shape has been newly imported/pasted/dropped checks the cells for stale links



385
386
387
388
389
390
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 385

def on_import
  # check for existence of non-included shapes
  @cells.delete_if do |shape|
    shape && !@child_shapes.include?(shape)
  end
end

#position_child_cell(child) ⇒ Object (protected)



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 429

def position_child_cell(child)
  crct = child.get_bounding_box
  # see if the child already had a cell in this grid (moving a child)
  child_index = @cells.index(child)
  # if the child intersects this box shape we look
  # for the cell it should go into (if any)
  if @cells.size>0 && intersects?(crct)
    # find the cell index where the new child is positioned above and in front of
    index = find_cell(crct)
    # now see where to put the new/moved child
    if index    # found a matching cell?
      # if the child being inserted already had a slot
      if child_index
        # if the newly found index equals the existing index there is nothing to do
        return if child_index == index
        # else clear the child's current cell; this provides support for reordering child shapes by dragging
        @cells[child_index] = nil
      end
      # insert/move the child
      unless insert_to_grid(index, child)
        # if failed to insert (max rows exceeded?) restore the child to it's previous cell
        if child_index
          @cells[child_index] = child
        else # or make the child a toplevel shape
          # move relative to current parent (if any)
          child.move_by(child.get_parent_shape.get_absolute_position) if child.get_parent_shape
          diagram.reparent_shape(child, nil)
        end
      end
      return # done
    end
  end
  # otherwise append
  # clear the child's current cell if already part of grid
  @cells[child_index] = nil if child_index
  # append
  unless append_to_grid(child)
    # if failed to append (max rows exceeded?) restore the child to it's previous cell
    if child_index
      @cells[child_index] = child
    else # or make the child a toplevel shape
      # move relative to current parent (if any)
      child.move_by(child.get_parent_shape.get_absolute_position) if child.get_parent_shape
      diagram.reparent_shape(child, nil)
    end
  end
end

#remove_from_grid(shape) ⇒ Shape?

Note:

Note this does not remove the shape as a child shape.

Remove given shape from the grid. Shifts any occupied cells beyond the cell containing the given shape to the previous lexicographic position.

Parameters:

  • shape (Shape)

    shape which should be removed

Returns:

  • (Shape, nil)

    removed shape or nil if not found



292
293
294
295
296
297
298
299
300
301
302
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 292

def remove_from_grid(shape)
  if @cells.delete(shape)
    # remove trailing empty cells
    @cells.pop until @cells.last
    # update row count
    @rows = @cells.size / @cols
    @rows += 1 if (@cells.size % @cols) > 0
    return shape
  end
  nil
end

#set_cell_space(cellspace) ⇒ Object Also known as: cell_space=

Set space between grid cells (managed shapes).

Parameters:

  • cellspace (Integer)

    Cellspace size



110
111
112
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 110

def set_cell_space(cellspace)
  @cell_space = cellspace
end

#set_dimensions(rows, cols) ⇒ Object

Set grid dimensions.

Parameters:

  • rows (Integer)

    Number of rows

  • cols (Integer)

    Number of columns



86
87
88
89
90
91
92
93
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 86

def set_dimensions(rows, cols)
  return if (new_size = rows * cols) == 0

  @rows = rows
  @cols = cols

  @cells.slice!(0, new_size) if new_size < @cells.size
end

#set_max_rows(num) ⇒ Integer Also known as: max_rows=

Sets the maximum number of rows for the grid (by default there this value is 0 == no maximum). In case the number of already managed cells exceeds the new maximum no change is made.

Returns:

  • (Integer)

    the active maximum



76
77
78
79
80
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 76

def set_max_rows(num)
  # only change as long as this does not invalidate already managed cells
  @max_rows = num unless (num * @cols) < @cells.size
  @max_rows
end

#update(recurse = true) ⇒ Object

Update shape (align all child shapes and resize it to fit them)



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 305

def update(recurse= true)
  # check for existence of de-assigned shapes
  @cells.delete_if do |shape|
    shape && !@child_shapes.include?(shape)
  end

  # check whether all child shapes are present in the cells array...
  @child_shapes.each do |child|
    unless @cells.include?(child)
      # see if we can match the position of the new child with the position of another
      # (previously assigned) managed shape
      position_child_cell(child)
    end
  end

  # do self-alignment
  do_alignment

  # fit the shape to its children
  fit_to_children unless has_style?(STYLE::NO_FIT_TO_CHILDREN)
  
  # do it recursively on all parent shapes
  get_parent_shape.update(recurse) if recurse && get_parent_shape
end

#update_rowsObject (protected)

update row count



393
394
395
396
397
398
# File 'lib/wx/shapes/shapes/grid_shape.rb', line 393

def update_rows
  # remove trailing empty cells (if any)
  @cells.pop until @cells.last
  @rows = @cells.size / @cols
  @rows += 1 if (@cells.size % @cols) > 0
end