Class: Shoes::Swt::TextBlock::Fitter
- Inherits:
-
Object
- Object
- Shoes::Swt::TextBlock::Fitter
- Defined in:
- shoes-swt/lib/shoes/swt/text_block/fitter.rb
Instance Attribute Summary collapse
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
Instance Method Summary collapse
- #available_space ⇒ Object
- #empty_segment ⇒ Object
- #first_element_on_line? ⇒ Boolean
-
#first_height(first_layout, first_text, height) ⇒ Object
If first text is empty, height may be smaller than an actual line in the current font.
- #fit_as_centered(width, height) ⇒ Object
- #fit_as_empty_first_layout(height) ⇒ Object
- #fit_as_one_layout(layout) ⇒ Object
- #fit_as_two_layouts(layout, height, width) ⇒ Object
- #fit_into_full_layouts(width, height) ⇒ Object
-
#fit_it_in ⇒ Object
Fitting text works by using either 1 or 2 layouts.
- #fits_in_one_layout?(layout, height) ⇒ Boolean
- #generate_layout(width, text) ⇒ Object
- #generate_second_layout(second_text) ⇒ Object
- #generate_two_layouts(first_layout, first_text, second_text, height) ⇒ Object
-
#initialize(text_block, current_position) ⇒ Fitter
constructor
A new instance of Fitter.
- #next_line_start ⇒ Object
- #no_space_in_first_layout?(width) ⇒ Boolean
- #on_new_line? ⇒ Boolean
- #position_two_segments(first_layout, second_layout, first_text, height) ⇒ Object
-
#split_text(layout, height) ⇒ Object
Splits the text into two pieces based on height.
-
#width_from_ancestor ⇒ Object
If we’re positioned outside our containing width, look up the parent chain until we find a width that accomodates us.
Constructor Details
#initialize(text_block, current_position) ⇒ Fitter
Returns a new instance of Fitter.
9 10 11 12 13 14 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 9 def initialize(text_block, current_position) @text_block = text_block @dsl = @text_block.dsl @parent = @dsl.parent @current_position = current_position end |
Instance Attribute Details
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
7 8 9 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 7 def parent @parent end |
Instance Method Details
#available_space ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 145 def available_space width = @dsl.desired_width height = next_line_start - @dsl.absolute_top - 1 if on_new_line? height = :unbounded # Try to find a parent container we fit in. # If that doesn't work, just bail with [0,0] so we don't crash. width = width_from_ancestor if width <= 0 return [0, 0] if width.negative? end [width, height] end |
#empty_segment ⇒ Object
187 188 189 190 191 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 187 def empty_segment segment = generate_layout(1, @dsl.text) segment.text = "" segment end |
#first_element_on_line? ⇒ Boolean
120 121 122 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 120 def first_element_on_line? @dsl.left - @dsl.margin_left <= 0 end |
#first_height(first_layout, first_text, height) ⇒ Object
If first text is empty, height may be smaller than an actual line in the current font. Take our pre-existing allowed height instead.
212 213 214 215 216 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 212 def first_height(first_layout, first_text, height) first_height = first_layout.bounds.height - first_layout.spacing first_height = height if first_text.empty? && height != :unbounded first_height end |
#fit_as_centered(width, height) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 106 def fit_as_centered(width, height) if first_element_on_line? segment = CenteredTextSegment.new(@dsl, width) [segment.position_at(@dsl.element_left, @dsl.element_top)] else position_two_segments( empty_segment, CenteredTextSegment.new(@dsl, @dsl.containing_width), "", height ) end end |
#fit_as_empty_first_layout(height) ⇒ Object
99 100 101 102 103 104 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 99 def fit_as_empty_first_layout(height) return [] if height == :unbounded || height.zero? height += ::Shoes::Slot::NEXT_ELEMENT_OFFSET generate_two_layouts(empty_segment, "", @dsl.text, height) end |
#fit_as_one_layout(layout) ⇒ Object
80 81 82 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 80 def fit_as_one_layout(layout) [layout.position_at(@dsl.element_left, @dsl.element_top)] end |
#fit_as_two_layouts(layout, height, width) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 84 def fit_as_two_layouts(layout, height, width) first_text, second_text = split_text(layout, height) # Since we regenerate layouts, we must dispose of first try here. layout.dispose first_layout = generate_layout(width, first_text) if second_text.empty? fit_as_one_layout(first_layout) else generate_two_layouts(first_layout, first_text, second_text, height) end end |
#fit_into_full_layouts(width, height) ⇒ Object
71 72 73 74 75 76 77 78 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 71 def fit_into_full_layouts(width, height) layout = generate_layout(width, @dsl.text) if fits_in_one_layout?(layout, height) fit_as_one_layout(layout) else fit_as_two_layouts(layout, height, width) end end |
#fit_it_in ⇒ Object
Fitting text works by using either 1 or 2 layouts
If the text fits in the height and width available, we use one layout.
| button | text layout 1 |
If if the text doesn’t fit into that space, then we’ll break it into two different layouts.
| button | text layout 1 |
| text layout 2 goes here| | in space |
^
If there wasn’t any available space in the first layout (very narrow) then we’ll make an empty first layout and flow to the second:
| big big big big button|| < empty layout 1 still present
| text layout 2 goes here| | in space |
^
When flowing, the position for the next element gets set to the end of the text in the second layout (shown as ^ in the diagram).
Stacks properly move to the next whole line as you’d expect.
51 52 53 54 55 56 57 58 59 60 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 51 def fit_it_in width, height = available_space if @dsl.centered? fit_as_centered(width, height) elsif no_space_in_first_layout?(width) fit_as_empty_first_layout(height) else fit_into_full_layouts(width, height) end end |
#fits_in_one_layout?(layout, height) ⇒ Boolean
66 67 68 69 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 66 def fits_in_one_layout?(layout, height) return true if height == :unbounded || layout.line_count == 1 layout.bounds.height <= height end |
#generate_layout(width, text) ⇒ Object
183 184 185 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 183 def generate_layout(width, text) TextSegment.new(@dsl, text, width) end |
#generate_second_layout(second_text) ⇒ Object
130 131 132 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 130 def generate_second_layout(second_text) generate_layout(@dsl.containing_width, second_text) end |
#generate_two_layouts(first_layout, first_text, second_text, height) ⇒ Object
124 125 126 127 128 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 124 def generate_two_layouts(first_layout, first_text, second_text, height) first_layout.fill_background = true second_layout = generate_second_layout(second_text) position_two_segments(first_layout, second_layout, first_text, height) end |
#next_line_start ⇒ Object
175 176 177 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 175 def next_line_start @current_position.next_line_start end |
#no_space_in_first_layout?(width) ⇒ Boolean
62 63 64 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 62 def no_space_in_first_layout?(width) width <= 0 end |
#on_new_line? ⇒ Boolean
179 180 181 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 179 def on_new_line? next_line_start <= @dsl.absolute_top end |
#position_two_segments(first_layout, second_layout, first_text, height) ⇒ Object
134 135 136 137 138 139 140 141 142 143 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 134 def position_two_segments(first_layout, second_layout, first_text, height) first_height = first_height(first_layout, first_text, height) [ first_layout.position_at(@dsl.element_left, @dsl.element_top), second_layout.position_at(parent.absolute_left + @dsl.margin_left, @dsl.element_top + first_height) ] end |
#split_text(layout, height) ⇒ Object
Splits the text into two pieces based on height. Allows one final line to exceed the requested height, which results in smoother flowing text between different sized fonts on a line.
196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 196 def split_text(layout, height) ending_offset = 0 height_so_far = 0 offsets = layout.line_offsets offsets[0...-1].each_with_index do |_, i| height_so_far += layout.get_line_bounds(i).height + TextBlock::NEXT_ELEMENT_OFFSET ending_offset = offsets[i + 1] break if height_so_far >= height end [layout.text[0...ending_offset], layout.text[ending_offset..-1]] end |
#width_from_ancestor ⇒ Object
If we’re positioned outside our containing width, look up the parent chain until we find a width that accomodates us.
163 164 165 166 167 168 169 170 171 172 173 |
# File 'shoes-swt/lib/shoes/swt/text_block/fitter.rb', line 163 def width_from_ancestor width = -1 current_ancestor = @dsl.parent until width.positive? || current_ancestor.nil? width = @dsl.desired_width(current_ancestor.width) break unless current_ancestor.respond_to?(:parent) current_ancestor = current_ancestor.parent end width end |