shoes-swt 4.0.0.pre3 → 4.0.0.pre4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/shoes-swt +2 -2
  3. data/lib/shoes/swt.rb +90 -72
  4. data/lib/shoes/swt/app.rb +5 -0
  5. data/lib/shoes/swt/click_listener.rb +0 -1
  6. data/lib/shoes/swt/color.rb +7 -7
  7. data/lib/shoes/swt/common/fill.rb +9 -3
  8. data/lib/shoes/swt/common/painter.rb +14 -11
  9. data/lib/shoes/swt/common/stroke.rb +9 -3
  10. data/lib/shoes/swt/dialog.rb +2 -2
  11. data/lib/shoes/swt/gradient.rb +13 -11
  12. data/lib/shoes/swt/image.rb +1 -3
  13. data/lib/shoes/swt/image_pattern.rb +2 -2
  14. data/lib/shoes/swt/line.rb +0 -2
  15. data/lib/shoes/swt/list_box.rb +6 -1
  16. data/lib/shoes/swt/progress.rb +9 -8
  17. data/lib/shoes/swt/redrawing_aspect.rb +1 -1
  18. data/lib/shoes/swt/shape.rb +3 -1
  19. data/lib/shoes/swt/shoes_layout.rb +38 -24
  20. data/lib/shoes/swt/text_block.rb +41 -12
  21. data/lib/shoes/swt/text_block/cursor_painter.rb +1 -1
  22. data/lib/shoes/swt/text_block/fitter.rb +8 -3
  23. data/lib/shoes/swt/text_block/painter.rb +9 -28
  24. data/lib/shoes/swt/text_block/text_segment.rb +16 -6
  25. data/lib/shoes/swt/text_block/text_segment_collection.rb +17 -5
  26. data/lib/shoes/swt/tooling/leak_hunter.rb +1 -1
  27. data/lib/shoes/swt/version.rb +1 -1
  28. data/shoes-swt.gemspec +2 -2
  29. data/spec/shoes/cli_spec.rb +1 -1
  30. data/spec/shoes/swt/app_spec.rb +15 -6
  31. data/spec/shoes/swt/color_spec.rb +8 -7
  32. data/spec/shoes/swt/common/painter_spec.rb +30 -5
  33. data/spec/shoes/swt/disposed_protection_spec.rb +0 -1
  34. data/spec/shoes/swt/font_spec.rb +2 -2
  35. data/spec/shoes/swt/gradient_spec.rb +5 -2
  36. data/spec/shoes/swt/image_pattern_spec.rb +11 -1
  37. data/spec/shoes/swt/list_box_spec.rb +24 -4
  38. data/spec/shoes/swt/mouse_move_listener_spec.rb +1 -1
  39. data/spec/shoes/swt/oval_spec.rb +0 -1
  40. data/spec/shoes/swt/progress_spec.rb +3 -4
  41. data/spec/shoes/swt/radio_group_spec.rb +4 -4
  42. data/spec/shoes/swt/radio_spec.rb +1 -1
  43. data/spec/shoes/swt/shape_spec.rb +8 -1
  44. data/spec/shoes/swt/shared_examples/paintable.rb +0 -1
  45. data/spec/shoes/swt/shared_examples/pattern.rb +2 -6
  46. data/spec/shoes/swt/shared_examples/swt_app_context.rb +0 -1
  47. data/spec/shoes/swt/shell_control_listener_spec.rb +4 -4
  48. data/spec/shoes/swt/shoes_layout_spec.rb +84 -0
  49. data/spec/shoes/swt/slot_spec.rb +1 -1
  50. data/spec/shoes/swt/spec_helper.rb +2 -0
  51. data/spec/shoes/swt/text_block/cursor_painter_spec.rb +2 -2
  52. data/spec/shoes/swt/text_block/fitter_spec.rb +19 -4
  53. data/spec/shoes/swt/text_block/painter_spec.rb +15 -189
  54. data/spec/shoes/swt/text_block/text_segment_collection_spec.rb +10 -5
  55. data/spec/shoes/swt/text_block/text_segment_spec.rb +4 -4
  56. data/spec/shoes/swt/text_block_spec.rb +28 -15
  57. metadata +8 -6
@@ -25,10 +25,14 @@ class Shoes
25
25
  @dsl.call_change_listeners
26
26
  end
27
27
  update_items
28
+
29
+ # Set initial selection without triggering callbacks!
30
+ choice = @dsl.style[:choose]
31
+ @real.text = choice if choice
28
32
  end
29
33
 
30
34
  def update_items
31
- @real.items = @dsl.items.map(&:to_s)
35
+ @real.items = @dsl.items.to_a.map(&:to_s)
32
36
  end
33
37
 
34
38
  def text
@@ -38,6 +42,7 @@ class Shoes
38
42
 
39
43
  def choose(item)
40
44
  @real.text = item
45
+ @dsl.call_change_listeners
41
46
  end
42
47
 
43
48
  def enabled(value)
@@ -5,9 +5,10 @@ class Shoes
5
5
  include Common::Remove
6
6
  include Common::Visibility
7
7
  include Common::UpdatePosition
8
+ include DisposedProtection
8
9
  include ::Shoes::BackendDimensionsDelegations
9
10
 
10
- attr_reader :parent, :real, :dsl
11
+ attr_reader :parent, :dsl
11
12
 
12
13
  def initialize(dsl, parent)
13
14
  @dsl = dsl
@@ -15,20 +16,20 @@ class Shoes
15
16
 
16
17
  @real = ::Swt::Widgets::ProgressBar.new(@parent.real,
17
18
  ::Swt::SWT::SMOOTH)
18
- @real.minimum = 0
19
- @real.maximum = 100
19
+ real.minimum = 0
20
+ real.maximum = 100
20
21
 
21
22
  if @dsl.element_width && @dsl.element_height
22
- @real.setSize dsl.element_width, dsl.element_height
23
+ real.setSize dsl.element_width, dsl.element_height
23
24
  else
24
- @real.pack
25
- @dsl.element_width = @real.size.x
26
- @dsl.element_height = @real.size.y
25
+ real.pack
26
+ @dsl.element_width = real.size.x
27
+ @dsl.element_height = real.size.y
27
28
  end
28
29
  end
29
30
 
30
31
  def fraction=(value)
31
- @real.selection = (value * 100).to_i unless @real.disposed?
32
+ real.selection = (value * 100).to_i
32
33
  end
33
34
  end
34
35
  end
@@ -25,7 +25,7 @@ class Shoes
25
25
 
26
26
  # These need to trigger a redraw
27
27
  SAME_POSITION = { Common::Visibility => [:update_visibility],
28
- Image => [:update_image],
28
+ Image => [:create_image],
29
29
  ::Shoes::Common::Style => [:update_style],
30
30
  ::Shoes::Common::Remove => [:remove],
31
31
  ::Shoes::Slot => [:mouse_hovered,
@@ -1,6 +1,8 @@
1
1
  class Shoes
2
2
  module Swt
3
3
  class Shape
4
+ include Common::Clickable
5
+ include Common::Visibility
4
6
  include Common::Remove
5
7
  include Common::Fill
6
8
  include Common::Stroke
@@ -41,7 +43,7 @@ class Shoes
41
43
  @element.cubic_to(cx1, cy1, cx2, cy2, x, y)
42
44
  end
43
45
 
44
- def arc(x, y, width, height, start_angle, arc_angle)
46
+ def arc_to(x, y, width, height, start_angle, arc_angle)
45
47
  @element.add_arc(x - (width / 2), y - (height / 2), width, height,
46
48
  -start_angle * 180 / ::Shoes::PI,
47
49
  (start_angle - arc_angle) * 180 / ::Shoes::PI)
@@ -4,42 +4,56 @@ class Shoes
4
4
  attr_accessor :gui_app
5
5
 
6
6
  def layout(*_dontcare)
7
- dsl_app = @gui_app.dsl
8
- height = dsl_app.height
9
- scrollable_height = dsl_app.top_slot.contents_alignment
10
- set_gui_size(height, scrollable_height)
11
-
12
- dsl_app.top_slot.width = dsl_app.width
13
- dsl_app.top_slot.height = [scrollable_height, height].max
7
+ height, scroll_height, is_scrolling = gather_height_info(@gui_app.dsl)
8
+ set_heights(@gui_app.dsl, @gui_app.real, height, scroll_height)
14
9
 
15
- vertical_bar = @gui_app.shell.getVerticalBar
16
- vertical_bar.setVisible(scrollable_height > height)
17
- if scrollable_height > height
18
- handle_scroll_bar(vertical_bar, height, scrollable_height)
10
+ if is_scrolling
11
+ show_scrollbar(vertical_scrollbar, height, scroll_height)
19
12
  else
20
- set_gui_location
13
+ hide_scrollbar(vertical_scrollbar, @gui_app.real)
21
14
  end
22
15
  end
23
16
 
24
17
  private
25
18
 
26
- def set_gui_size(height, scrollable_height)
27
- width = @gui_app.dsl.width
28
- maximum_height = [scrollable_height, height].max
29
- size = @gui_app.real.compute_trim 0, 0, width, maximum_height
30
- @gui_app.real.set_size(size.width, size.height)
19
+ def gather_height_info(dsl_app)
20
+ height = dsl_app.height
21
+ scroll_height = dsl_app.top_slot.contents_alignment
22
+ [height, scroll_height, scroll_height > height]
31
23
  end
32
24
 
33
- def handle_scroll_bar(vertical_bar, height, scrollable_height)
34
- vertical_bar.setThumb height * height / scrollable_height
35
- vertical_bar.setMaximum scrollable_height - height + vertical_bar.getThumb
36
- vertical_bar.increment = 10
25
+ def set_heights(dsl_app, real_app, height, scroll_height)
26
+ maximum_height = [height, scroll_height].max
27
+ set_real_size(dsl_app, real_app, maximum_height)
28
+ set_top_slot_size(dsl_app, maximum_height)
37
29
  end
38
30
 
39
- def set_gui_location
40
- location = @gui_app.real.getLocation
31
+ def set_real_size(dsl_app, real_app, maximum_height)
32
+ size = real_app.compute_trim 0, 0, dsl_app.width, maximum_height
33
+ real_app.set_size(size.width, size.height)
34
+ end
35
+
36
+ def set_top_slot_size(dsl_app, maximum_height)
37
+ dsl_app.top_slot.width = dsl_app.width
38
+ dsl_app.top_slot.height = maximum_height
39
+ end
40
+
41
+ def show_scrollbar(scrollbar, height, scroll_height)
42
+ scrollbar.visible = true
43
+ scrollbar.thumb = height * height / scroll_height
44
+ scrollbar.maximum = scroll_height - height + scrollbar.thumb
45
+ scrollbar.increment = 10
46
+ end
47
+
48
+ def hide_scrollbar(scrollbar, real_app)
49
+ scrollbar.visible = false
50
+ location = real_app.location
41
51
  location.y = 0
42
- @gui_app.real.setLocation location
52
+ real_app.location = location
53
+ end
54
+
55
+ def vertical_scrollbar
56
+ @gui_app.shell.vertical_bar
43
57
  end
44
58
  end
45
59
  end
@@ -7,15 +7,16 @@ class Shoes
7
7
  include ::Shoes::BackendDimensionsDelegations
8
8
 
9
9
  DEFAULT_SPACING = 4
10
+ NEXT_ELEMENT_OFFSET = 1
10
11
 
11
12
  attr_reader :dsl, :app
12
13
  attr_accessor :segments
13
14
 
14
15
  def initialize(dsl, app)
15
- @dsl = dsl
16
- @app = app
17
- @segments = []
18
- @painter = Painter.new @dsl
16
+ @dsl = dsl
17
+ @app = app
18
+ @segments = TextSegmentCollection.new(@dsl, [], default_text_styles)
19
+ @painter = Painter.new @dsl
19
20
  @app.add_paint_listener @painter
20
21
  end
21
22
 
@@ -35,16 +36,45 @@ class Shoes
35
36
 
36
37
  def contents_alignment(current_position)
37
38
  dispose_existing_segments
38
- @segments = Fitter.new(self, current_position).fit_it_in
39
+ raw_segments = Fitter.new(self, current_position).fit_it_in
39
40
 
40
- set_absolutes_on_dsl(current_position)
41
- set_calculated_sizes
41
+ if raw_segments && raw_segments.any?
42
+ @segments = TextSegmentCollection.new(@dsl, raw_segments, default_text_styles)
43
+ set_absolutes_on_dsl(current_position)
44
+ set_calculated_sizes
45
+ else
46
+ @segments = TextSegmentCollection.new(@dsl, [], default_text_styles)
47
+ end
48
+ end
49
+
50
+ def default_text_styles
51
+ style = @dsl.style
52
+
53
+ {
54
+ fg: style[:fg],
55
+ bg: style[:bg],
56
+ strikecolor: style[:strikecolor],
57
+ undercolor: style[:undercolor],
58
+ font_detail: {
59
+ name: @dsl.font,
60
+ size: @dsl.size,
61
+ styles: [::Swt::SWT::NORMAL]
62
+ }
63
+ }
42
64
  end
43
65
 
44
66
  def adjust_current_position(current_position)
67
+ current_position.y = @dsl.absolute_bottom + NEXT_ELEMENT_OFFSET
68
+
45
69
  last_segment = segments.last
46
- current_position.y = @dsl.absolute_bottom
47
- current_position.y -= last_segment.last_line_height unless @bumped_to_next_line
70
+ if last_segment && !@bumped_to_next_line
71
+ # Not quite sure why this is necessary. Could be a problem in some
72
+ # other part of positioning, or something about how text layouts
73
+ # actually draw themselves.
74
+ current_position.x -= 1
75
+
76
+ current_position.y -= last_segment.last_line_height
77
+ end
48
78
  end
49
79
 
50
80
  def set_absolutes_on_dsl(current_position)
@@ -65,10 +95,10 @@ class Shoes
65
95
  last_segment = segments.last
66
96
 
67
97
  @dsl.absolute_right = starting_left + last_segment.last_line_width +
68
- margin_right
98
+ margin_right - NEXT_ELEMENT_OFFSET
69
99
 
70
100
  @dsl.absolute_bottom = starting_top + last_segment.height +
71
- margin_top + margin_bottom
101
+ margin_top + margin_bottom - NEXT_ELEMENT_OFFSET
72
102
  end
73
103
 
74
104
  def bump_absolutes_to_next_line
@@ -104,7 +134,6 @@ class Shoes
104
134
  end
105
135
 
106
136
  def dispose_existing_segments
107
- @segments.map(&:dispose)
108
137
  @segments.clear
109
138
  end
110
139
 
@@ -42,7 +42,7 @@ class Shoes
42
42
  end
43
43
 
44
44
  def remove_textcursor
45
- return unless @text_block_dsl.has_textcursor?
45
+ return unless @text_block_dsl.textcursor?
46
46
 
47
47
  @text_block_dsl.textcursor.remove
48
48
  @text_block_dsl.textcursor = nil
@@ -95,6 +95,10 @@ class Shoes
95
95
  end
96
96
 
97
97
  def fit_as_empty_first_layout(height)
98
+ if height == :unbounded || height == 0
99
+ return []
100
+ end
101
+
98
102
  height += ::Shoes::Slot::NEXT_ELEMENT_OFFSET
99
103
  generate_two_layouts(empty_segment, "", @dsl.text, height)
100
104
  end
@@ -117,6 +121,7 @@ class Shoes
117
121
  end
118
122
 
119
123
  def generate_two_layouts(first_layout, first_text, second_text, height)
124
+ first_layout.fill_background = true
120
125
  second_layout = generate_second_layout(second_text)
121
126
  position_two_segments(first_layout, second_layout, first_text, height)
122
127
  end
@@ -132,7 +137,7 @@ class Shoes
132
137
  first_layout.position_at(@dsl.element_left,
133
138
  @dsl.element_top),
134
139
  second_layout.position_at(parent.absolute_left + @dsl.margin_left,
135
- @dsl.element_top + first_height + 1)
140
+ @dsl.element_top + first_height)
136
141
  ]
137
142
  end
138
143
 
@@ -193,10 +198,10 @@ class Shoes
193
198
 
194
199
  offsets = layout.line_offsets
195
200
  offsets[0...-1].each_with_index do |_, i|
196
- height_so_far += layout.line_bounds(i).height
201
+ height_so_far += layout.line_bounds(i).height + TextBlock::NEXT_ELEMENT_OFFSET
197
202
  ending_offset = offsets[i + 1]
198
203
 
199
- break if height_so_far > height
204
+ break if height_so_far >= height
200
205
  end
201
206
  [layout.text[0...ending_offset], layout.text[ending_offset..-1]]
202
207
  end
@@ -6,40 +6,21 @@ class Shoes
6
6
  include Common::Resource
7
7
 
8
8
  attr_reader :app
9
+
9
10
  def initialize(dsl)
10
11
  @dsl = dsl
11
- @style = @dsl.style
12
- @app = @dsl.app.gui
13
12
  end
14
13
 
15
14
  def paintControl(paint_event)
16
- reset_graphics_context(paint_event.gc)
17
- return if @dsl.hidden?
18
-
19
- draw_layouts(paint_event.gc)
20
- end
15
+ # See #636 for discussion, contents_alignment may not run or if the
16
+ # space is very narrow we might squish things down to be very narrow.
17
+ # If paint is triggered then, code later on will crash.
18
+ return if @dsl.hidden? ||
19
+ @dsl.gui.segments.nil? ||
20
+ @dsl.gui.segments.empty?
21
21
 
22
- def draw_layouts(graphic_context)
23
- layouts = TextSegmentCollection.new(@dsl,
24
- @dsl.gui.segments,
25
- default_text_styles)
26
- layouts.paint_control(graphic_context)
27
- end
28
-
29
- private
30
-
31
- def default_text_styles
32
- {
33
- fg: @style[:fg],
34
- bg: @style[:bg],
35
- strikecolor: @style[:strikecolor],
36
- undercolor: @style[:undercolor],
37
- font_detail: {
38
- name: @dsl.font,
39
- size: @dsl.size,
40
- styles: [::Swt::SWT::NORMAL]
41
- }
42
- }
22
+ reset_graphics_context(paint_event.gc)
23
+ @dsl.gui.segments.paint_control(paint_event.gc)
43
24
  end
44
25
  end
45
26
  end
@@ -14,7 +14,8 @@ class Shoes
14
14
  class TextSegment
15
15
  DEFAULT_SPACING = 4
16
16
 
17
- attr_reader :layout, :element_left, :element_top
17
+ attr_reader :layout, :element_left, :element_top
18
+ attr_accessor :fill_background
18
19
 
19
20
  extend Forwardable
20
21
  def_delegators :@layout, :text, :text=, :bounds, :width, :spacing,
@@ -23,11 +24,14 @@ class Shoes
23
24
  def initialize(dsl, text, width)
24
25
  @dsl = dsl
25
26
  @layout = ::Swt::TextLayout.new Shoes.display
27
+ @fill_background = false
28
+
26
29
  @font_factory = TextFontFactory.new
27
30
  @style_factory = TextStyleFactory.new
31
+ @color_factory = ColorFactory.new
28
32
 
29
33
  layout.text = text
30
- layout.width = width unless layout_fits_in?(width)
34
+ layout.width = width
31
35
  style_from(font_styling, @dsl.style)
32
36
  end
33
37
 
@@ -35,6 +39,7 @@ class Shoes
35
39
  @layout.dispose unless @layout.disposed?
36
40
  @font_factory.dispose
37
41
  @style_factory.dispose
42
+ @color_factory.dispose
38
43
  end
39
44
 
40
45
  def position_at(element_left, element_top)
@@ -78,10 +83,6 @@ class Shoes
78
83
  }
79
84
  end
80
85
 
81
- def layout_fits_in?(width)
82
- layout.bounds.width <= width
83
- end
84
-
85
86
  def height
86
87
  layout.bounds.height - layout.spacing
87
88
  end
@@ -95,6 +96,15 @@ class Shoes
95
96
  end
96
97
 
97
98
  def draw(graphics_context)
99
+ # Why not use TextLayout's background? Unfortunately it doesn't draw
100
+ # all the way to the edges--just around the literal text. This leaves
101
+ # things jagged when we flow, so do it ourselves.
102
+ if fill_background && @dsl.style[:fill]
103
+ background_color = @color_factory.create(@dsl.style[:fill])
104
+ background_color.apply_as_fill(graphics_context)
105
+ graphics_context.fill_rectangle(element_left, element_top, width, height)
106
+ end
107
+
98
108
  layout.draw(graphics_context, element_left, element_top)
99
109
  end
100
110
 
@@ -3,7 +3,8 @@ class Shoes
3
3
  class TextBlock
4
4
  class TextSegmentCollection
5
5
  extend Forwardable
6
- def_delegators :@segments, :length
6
+ def_delegators :@segments, :length, :last, :inject,
7
+ :one?, :any?, :empty?
7
8
 
8
9
  attr_reader :dsl, :default_text_styles
9
10
 
@@ -13,14 +14,26 @@ class Shoes
13
14
  @default_text_styles = default_text_styles
14
15
  end
15
16
 
17
+ def clear
18
+ @segments.map(&:dispose)
19
+ @segments.clear
20
+ end
21
+
16
22
  def paint_control(graphic_context)
17
- style_from(dsl.style)
18
- style_segment_ranges(dsl.text_styles)
19
- create_links(dsl.text_styles)
23
+ ensure_styled
20
24
  draw(graphic_context)
21
25
  draw_cursor
22
26
  end
23
27
 
28
+ def ensure_styled
29
+ return if @styled
30
+
31
+ style_from(@dsl.style)
32
+ style_segment_ranges(@dsl.text_styles)
33
+ create_links(@dsl.text_styles)
34
+ @styled = true
35
+ end
36
+
24
37
  def style_from(style)
25
38
  @segments.each do |segment|
26
39
  segment.style_from(default_text_styles, style)
@@ -82,7 +95,6 @@ class Shoes
82
95
  # be in either, or both, of the segments. This method figures out which
83
96
  # segments apply, and what the relative ranges within each segment to use.
84
97
  def segment_ranges(text_range)
85
- return [] unless @segments.first # TODO WTF #636
86
98
  return [] unless text_range.any?
87
99
 
88
100
  first_text = @segments.first.text