@@ -23,7 +23,7 @@ module Gruff
2323 # A common base class inherited from class of drawing a graph.
2424 class Base
2525 # Space around text elements. Mostly used for vertical spacing.
26- LEGEND_MARGIN = 20 .0
26+ LEGEND_MARGIN = 10 .0
2727 TITLE_MARGIN = 20.0
2828 LABEL_MARGIN = 15.0
2929 DEFAULT_MARGIN = 20.0
@@ -48,9 +48,6 @@ class Base
4848 # Blank space below the title. Default is +20+.
4949 attr_writer :title_margin #: Float | Integer
5050
51- # Blank space below the legend. Default is +20+.
52- attr_writer :legend_margin #: Float | Integer
53-
5451 # Truncates labels if longer than max specified.
5552 attr_writer :label_max_size #: Float | Integer
5653
@@ -115,13 +112,16 @@ class Base
115112 # first. This does not affect the legend. Default is +false+.
116113 attr_writer :sorted_drawing #: bool
117114
118- # Display the legend under the graph . Default is +false +.
119- attr_writer :legend_at_bottom #: bool
115+ # Blank space below the legend . Default is +20 +.
116+ attr_writer :legend_margin #: Float | Integer
120117
121- # Optionally set the size of the colored box by each item in the legend.
122- # Default is +20.0+.
123- #
124- # Will be scaled down if graph is smaller than 800px wide.
118+ # Set the vertical spacing between individual legend items. Default is +5.0+.
119+ attr_writer :legend_spacing #: Float | Integer
120+
121+ # Set the inner padding between the legend frame and its contents. Default is +7.0+.
122+ attr_writer :legend_padding #: Float | Integer
123+
124+ # Optionally set the size of the colored box by each item in the legend. Default is +20.0+.
125125 attr_writer :legend_box_size #: Float | Integer
126126
127127 # If one numerical argument is given, the graph is drawn at 4/3 ratio
@@ -190,19 +190,22 @@ def initialize_attributes
190190
191191 @title_font = Gruff ::Font . new ( size : 36.0 , bold : true )
192192 @marker_font = Gruff ::Font . new ( size : 21.0 )
193- @legend_font = Gruff ::Font . new ( size : 20 .0)
193+ @legend_font = Gruff ::Font . new ( size : 12 .0)
194194 @no_data_font = Gruff ::Font . new ( size : 80.0 )
195195
196196 @label_margin = LABEL_MARGIN
197197 @top_margin = @bottom_margin = @left_margin = @right_margin = DEFAULT_MARGIN
198- @legend_margin = LEGEND_MARGIN
199198 @title_margin = TITLE_MARGIN
200199
201- @legend_box_size = 20.0
200+ @legend_margin = LEGEND_MARGIN
201+ @legend_spacing = 5.0
202+ @legend_padding = 7.0
203+ @legend_box_size = 10.0
204+ @legend_position = :top_right
202205
203206 @no_data_message = 'No Data'
204207
205- @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = false
208+ @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = false
206209 @label_max_size = 0
207210 @label_truncation_style = :absolute
208211 @label_rotation = 0
@@ -255,13 +258,34 @@ def label_rotation=(rotation)
255258 @label_rotation = rotation . to_f
256259 end
257260
261+ # Set the corner position of the floating legend. Accepts +:top_right+ (default),
262+ # +:top_left+, +:bottom_right+, or +:bottom_left+.
263+ #
264+ # @param position [Symbol] the position.
265+ # @rbs position: Symbol
266+ # @rbs return: void
267+ def legend_position = ( position )
268+ unless %i[ top_right top_left bottom_right bottom_left ] . include? ( position )
269+ raise ArgumentError , 'Invalid legend position. Must be :top_right, :top_left, :bottom_right, or :bottom_left.'
270+ end
271+
272+ @legend_position = position
273+ end
274+
258275 # Height of staggering between labels.
259276 # @deprecated
260277 # @rbs return: void
261278 def label_stagger_height = ( _value )
262279 warn '#label_stagger_height= is deprecated. It is no longer effective.'
263280 end
264281
282+ # Set the legend position to the bottom of the graph.
283+ # @deprecated
284+ # @rbs return: void
285+ def legend_at_bottom = ( _value )
286+ warn '#legend_at_bottom= is deprecated. It is no longer effective.'
287+ end
288+
265289 # Set the large title of the graph displayed at the top.
266290 # You can draw a multi-line title by putting a line break in the string
267291 # or by setting an array as argument.
@@ -629,11 +653,11 @@ def draw
629653
630654 setup_drawing
631655
632- draw_legend
633656 draw_line_markers
634657 draw_axis_labels
635658 draw_title
636659 draw_graph
660+ draw_legend
637661 end
638662
639663 protected
@@ -822,46 +846,57 @@ def center(size)
822846 def draw_legend
823847 return if @hide_legend
824848
825- legend_labels = store . data . map ( &:label )
826- legend_square_width = @legend_box_size # small square with color of this item
827- legend_label_lines = calculate_legend_label_widths_for_each_line ( legend_labels , legend_square_width )
828- line_height = [ legend_caps_height , legend_square_width ] . max + @legend_margin
829-
830- current_y_offset = begin
831- if @legend_at_bottom
832- @graph_bottom + @legend_margin + labels_caps_height + @label_margin + ( @x_axis_label ? ( @label_margin * 2 ) + marker_caps_height : 0 )
833- else
834- hide_title? ? @top_margin + @title_margin : @top_margin + @title_margin + title_caps_height
835- end
849+ legend_labels = store . data . map ( &:label ) . reject ( &:empty? )
850+ return if legend_labels . empty?
851+
852+ line_height = [ legend_caps_height , @legend_box_size ] . max + @legend_spacing
853+ max_legend_label_width = legend_labels . map { |l | calculate_width ( @legend_font , l ) } . max || 0.0
854+
855+ legend_content_width = ( @legend_box_size * 1.5 ) + max_legend_label_width
856+ legend_content_height = ( line_height * ( legend_labels . size - 1 ) ) + @legend_box_size
857+
858+ case @legend_position
859+ when :top_left
860+ current_x_offset = @graph_left + @legend_margin
861+ current_y_offset = @graph_top + @legend_margin + @legend_spacing
862+ when :bottom_right
863+ current_x_offset = @graph_right - @legend_margin - legend_content_width
864+ current_y_offset = @graph_bottom - @legend_margin - legend_content_height
865+ when :bottom_left
866+ current_x_offset = @graph_left + @legend_margin
867+ current_y_offset = @graph_bottom - @legend_margin - legend_content_height
868+ else # :top_right (default)
869+ current_x_offset = @graph_right - @legend_margin - legend_content_width
870+ current_y_offset = @graph_top + @legend_margin + @legend_spacing
836871 end
837872
838- index = 0
839- legend_label_lines . each do | ( legend_labels_width , legend_labels_line ) |
840- current_x_offset = center ( legend_labels_width )
841-
842- legend_labels_line . each do | legend_label |
843- unless legend_label . empty?
844- legend_label_width = calculate_width ( @legend_font , legend_label )
845-
846- # Draw label
847- text_renderer = Gruff :: Renderer :: Text . new ( renderer , legend_label , font : @legend_font )
848- text_renderer . add_to_render_queue ( legend_label_width ,
849- legend_square_width ,
850- current_x_offset + ( legend_square_width * 1.7 ) ,
851- current_y_offset ,
852- Magick :: CenterGravity )
853-
854- # Now draw box with color of this dataset
855- rect_renderer = Gruff :: Renderer :: Rectangle . new ( renderer , color : store . data [ index ] . color )
856- rect_renderer . render ( current_x_offset ,
857- current_y_offset ,
858- current_x_offset + legend_square_width ,
859- current_y_offset + legend_square_width )
860-
861- current_x_offset += legend_label_width + ( legend_square_width * 2.7 )
862- end
863- index += 1
864- end
873+ legend_items_end_x = current_x_offset + legend_content_width
874+ legend_items_end_y = current_y_offset + legend_content_height
875+
876+ frame_renderer = Gruff :: Renderer :: Rectangle . new ( renderer , color : @marker_color , width : 1.1 , opacity : 0.2 , round : true )
877+ frame_renderer . render ( current_x_offset - @legend_padding ,
878+ current_y_offset - @legend_padding ,
879+ legend_items_end_x + @legend_padding ,
880+ legend_items_end_y + @legend_padding )
881+
882+ store . data . each do | data_row |
883+ legend_label = data_row . label
884+ next if legend_label . empty?
885+
886+ legend_label_width = calculate_width ( @legend_font , legend_label )
887+
888+ text_renderer = Gruff :: Renderer :: Text . new ( renderer , legend_label , font : @legend_font )
889+ text_renderer . add_to_render_queue ( legend_label_width ,
890+ @legend_box_size ,
891+ current_x_offset + ( @legend_box_size * 1.5 ) ,
892+ current_y_offset ,
893+ Magick :: CenterGravity )
894+
895+ rect_renderer = Gruff :: Renderer :: Rectangle . new ( renderer , color : data_row . color )
896+ rect_renderer . render ( current_x_offset ,
897+ current_y_offset ,
898+ current_x_offset + @legend_box_size ,
899+ current_y_offset + @legend_box_size )
865900
866901 current_y_offset += line_height
867902 end
@@ -1123,14 +1158,12 @@ def setup_top_margin
11231158 # When @hide title, leave a title_margin space for aesthetics.
11241159 # Same with @hide_legend
11251160 @top_margin +
1126- ( hide_title? ? @title_margin : title_caps_height + @title_margin ) +
1127- ( @hide_legend || @legend_at_bottom ? @legend_margin : calculate_legend_height + @legend_margin )
1161+ ( hide_title? ? @title_margin : title_caps_height + @title_margin )
11281162 end
11291163
11301164 # @rbs return: Float
11311165 def setup_bottom_margin
11321166 graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + labels_caps_height + @label_margin
1133- graph_bottom_margin += ( calculate_legend_height + @legend_margin ) if @legend_at_bottom
11341167
11351168 x_axis_label_height = @x_axis_label . nil? ? 0.0 : marker_caps_height + ( @label_margin * 2 )
11361169 @raw_rows - graph_bottom_margin - x_axis_label_height
@@ -1211,42 +1244,6 @@ def y_axis_label(value, increment)
12111244 end
12121245 end
12131246
1214- # @rbs legend_labels: Array[String]
1215- # @rbs legend_square_width: Float | Integer
1216- # @rbs return: Array[Array[Float | Integer]]
1217- def calculate_legend_label_widths_for_each_line ( legend_labels , legend_square_width )
1218- label_widths = [ [ ] ]
1219- label_lines = [ [ ] ]
1220- legend_labels . each do |label |
1221- if label . empty?
1222- label_width = 0.0
1223- else
1224- width = calculate_width ( @legend_font , label )
1225- label_width = width + ( legend_square_width * 2.7 )
1226- end
1227- label_widths . last . push label_width
1228- label_lines . last . push label
1229-
1230- if label_widths . last . sum > ( @raw_columns * 0.9 )
1231- label_widths . push [ label_widths . last . pop ]
1232- label_lines . push [ label_lines . last . pop ]
1233- end
1234- end
1235-
1236- label_widths . map ( &:sum ) . zip ( label_lines )
1237- end
1238-
1239- # @rbs return: Float
1240- def calculate_legend_height
1241- return 0.0 if @hide_legend
1242-
1243- legend_labels = store . data . map ( &:label )
1244- legend_label_lines = calculate_legend_label_widths_for_each_line ( legend_labels , @legend_box_size )
1245- line_height = [ legend_caps_height , @legend_box_size ] . max
1246-
1247- ( line_height * legend_label_lines . count ) + ( @legend_margin * ( legend_label_lines . count - 1 ) )
1248- end
1249-
12501247 # Returns the height of the capital letter 'X' for the current font and
12511248 # size.
12521249 #
0 commit comments