Line data Source code
1 : /* File: gui_sketch_result_list.c; Copyright and License: see below */
2 :
3 : #include "sketch/gui_sketch_result_list.h"
4 : #include "geometry/geometry_rectangle.h"
5 : #include "utf8stringbuf/utf8stringbuf.h"
6 : #include "u8/u8_trace.h"
7 : #include "u8/u8_log.h"
8 : #include "u8/u8_i32.h"
9 : #include <gdk/gdk.h>
10 :
11 : static const int GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH = -1; /*!< pango automatically determines the string length */
12 : static const int GUI_SKETCH_RESULT_LIST_PANGO_UNLIMITED_WIDTH = -1;
13 : static const int OBJ_GAP = 4;
14 :
15 0 : void gui_sketch_result_list_init( gui_sketch_result_list_t *this_,
16 : gui_resources_t *resources,
17 : gui_sketch_texture_t *texture_downloader )
18 : {
19 0 : U8_TRACE_BEGIN();
20 0 : assert( resources != NULL );
21 0 : assert( texture_downloader != NULL );
22 :
23 0 : DATA_SEARCH_RESULT_LIST_INIT( &((*this_).result_list), (*this_).result_list_buf );
24 :
25 0 : (*this_).visible = false;
26 0 : shape_int_rectangle_init( &((*this_).bounds), 0, 0, 0, 0 );
27 :
28 0 : (*this_).element_count = 0;
29 :
30 0 : gui_sketch_style_init( &((*this_).sketch_style) );
31 0 : gui_sketch_marker_init( &((*this_).sketch_marker), true );
32 0 : (*this_).resources = resources;
33 0 : gui_type_resource_list_init( &((*this_).selector), resources );
34 0 : (*this_).texture_downloader = texture_downloader;
35 :
36 0 : U8_TRACE_END();
37 0 : }
38 :
39 0 : void gui_sketch_result_list_destroy( gui_sketch_result_list_t *this_ )
40 : {
41 0 : U8_TRACE_BEGIN();
42 :
43 0 : (*this_).texture_downloader = NULL;
44 0 : gui_type_resource_list_destroy( &((*this_).selector) );
45 0 : (*this_).resources = NULL;
46 0 : gui_sketch_marker_destroy( &((*this_).sketch_marker) );
47 0 : gui_sketch_style_destroy( &((*this_).sketch_style) );
48 :
49 0 : gui_sketch_result_list_invalidate_data( this_ );
50 :
51 0 : shape_int_rectangle_destroy( &((*this_).bounds) );
52 :
53 0 : U8_TRACE_END();
54 0 : }
55 :
56 0 : void gui_sketch_result_list_do_layout( gui_sketch_result_list_t *this_, cairo_t *cr )
57 : {
58 0 : U8_TRACE_BEGIN();
59 :
60 : /* create the font_layout */
61 : PangoLayout *font_layout;
62 : {
63 0 : font_layout = pango_cairo_create_layout (cr);
64 : const PangoFontDescription *const std_font
65 0 : = gui_sketch_style_get_standard_font_description( &((*this_).sketch_style ) );
66 0 : pango_layout_set_font_description( font_layout, std_font );
67 : }
68 :
69 0 : int32_t y_pos = shape_int_rectangle_get_top( &((*this_).bounds) );
70 :
71 0 : const unsigned int count = data_search_result_list_get_length( &((*this_).result_list) );
72 0 : assert( count <= GUI_SKETCH_RESULT_LIST_MAX_ELEMENTS );
73 0 : (*this_).element_count = count;
74 0 : for ( unsigned int idx = 0; idx < count; idx ++ )
75 : {
76 0 : const data_search_result_t *result = data_search_result_list_get_const( &((*this_).result_list), idx );
77 0 : pos_search_result_init( &((*this_).element_pos[idx]), result );
78 0 : gui_sketch_result_list_private_layout_element( this_, &((*this_).element_pos[idx]), &y_pos, font_layout );
79 : }
80 :
81 : /* release the font_layout */
82 0 : g_object_unref(font_layout);
83 :
84 0 : U8_TRACE_END();
85 0 : }
86 :
87 0 : void gui_sketch_result_list_private_layout_element ( gui_sketch_result_list_t *this_,
88 : pos_search_result_t *element,
89 : int32_t *io_y_pos,
90 : PangoLayout *font_layout )
91 : {
92 0 : U8_TRACE_BEGIN();
93 0 : assert( NULL != element );
94 0 : assert( NULL != io_y_pos );
95 0 : assert( NULL != font_layout );
96 :
97 0 : int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
98 0 : uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
99 0 : const data_search_result_t *result = pos_search_result_get_data_const( element );
100 :
101 : /* determine icon dimensions */
102 : {
103 0 : const data_type_t result_type = data_search_result_get_match_type( result );
104 : gui_type_resource_t *const type_data
105 0 : = gui_type_resource_list_get_type ( &((*this_).selector), result_type );
106 0 : GdkTexture *const icon = gui_type_resource_get_icon( type_data );
107 0 : const double icon_width = gdk_texture_get_width( icon );
108 0 : const double icon_height = gdk_texture_get_height( icon );
109 :
110 0 : const shape_int_rectangle_t new_icon_box = (shape_int_rectangle_t) {
111 0 : .left=left+OBJ_GAP,
112 0 : .top=(*io_y_pos)+OBJ_GAP,
113 0 : .width=icon_width+0.999,
114 0 : .height=icon_height+0.999 };
115 0 : pos_search_result_set_icon_box( element, &new_icon_box );
116 : }
117 :
118 : /* determine label dimensions */
119 0 : const shape_int_rectangle_t *const icon_box = pos_search_result_get_icon_box_const( element );
120 : {
121 0 : int_fast32_t proposed_pango_width = width - shape_int_rectangle_get_width(icon_box) - (4*OBJ_GAP);
122 0 : pango_layout_set_text( font_layout,
123 : data_search_result_get_match_name_const( result ),
124 : GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH
125 : );
126 0 : pango_layout_set_width(font_layout, proposed_pango_width * PANGO_SCALE );
127 : int text_width;
128 : int text_height;
129 0 : pango_layout_get_pixel_size(font_layout, &text_width, &text_height);
130 :
131 0 : int_fast32_t x_pos = shape_int_rectangle_get_right(icon_box);
132 :
133 0 : const shape_int_rectangle_t new_label_box = (shape_int_rectangle_t) {
134 0 : .left=x_pos+OBJ_GAP,
135 0 : .top=(*io_y_pos)+OBJ_GAP,
136 : .width=text_width,
137 : .height=text_height };
138 0 : pos_search_result_set_label_box( element, &new_label_box );
139 : }
140 :
141 0 : const shape_int_rectangle_t *const label_box = pos_search_result_get_label_box_const( element );
142 : *io_y_pos
143 0 : = u8_i32_max2( shape_int_rectangle_get_bottom(icon_box), shape_int_rectangle_get_bottom(label_box) )
144 0 : + OBJ_GAP;
145 :
146 0 : U8_TRACE_END();
147 0 : }
148 :
149 : static const double GREY_R = 0.8;
150 : static const double GREY_G = 0.8;
151 : static const double GREY_B = 0.8;
152 : static const double GREY_A = 1.0;
153 :
154 0 : void gui_sketch_result_list_draw ( gui_sketch_result_list_t *this_, const gui_marked_set_t *marker, cairo_t *cr )
155 : {
156 0 : U8_TRACE_BEGIN();
157 0 : assert( NULL != marker );
158 0 : assert( NULL != cr );
159 :
160 0 : if ( (*this_).visible )
161 : {
162 0 : PangoLayout *font_layout = pango_cairo_create_layout (cr);
163 : {
164 : const PangoFontDescription *const std_font
165 0 : = gui_sketch_style_get_standard_font_description( &((*this_).sketch_style ) );
166 0 : pango_layout_set_font_description ( font_layout, std_font );
167 : }
168 :
169 : /* draw background */
170 : {
171 0 : const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
172 0 : const int_fast32_t top = shape_int_rectangle_get_top( &((*this_).bounds) );
173 0 : const uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
174 0 : const uint_fast32_t height = shape_int_rectangle_get_height( &((*this_).bounds) );
175 :
176 0 : cairo_set_source_rgba( cr, GREY_R, GREY_G, GREY_B, GREY_A );
177 0 : cairo_rectangle ( cr, left, top, width, height );
178 0 : cairo_fill (cr);
179 : }
180 :
181 : /* draw icons and text */
182 0 : const unsigned int count = (*this_).element_count;
183 0 : assert( count <= GUI_SKETCH_RESULT_LIST_MAX_ELEMENTS );
184 0 : if ( count == 0 )
185 : {
186 0 : const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
187 0 : const int_fast32_t top = shape_int_rectangle_get_top( &((*this_).bounds) );
188 0 : GdkTexture *undef_icon = gui_resources_get_type_undef( (*this_).resources );
189 0 : double icon_width = gdk_texture_get_width ( undef_icon );
190 :
191 : /* draw text first, use the above set color and font */
192 0 : const GdkRGBA std_color = gui_sketch_style_get_standard_color( &((*this_).sketch_style) );
193 0 : cairo_set_source_rgba( cr, std_color.red, std_color.green, std_color.blue, std_color.alpha );
194 0 : cairo_move_to( cr, left+OBJ_GAP+icon_width, top+OBJ_GAP );
195 0 : pango_layout_set_text( font_layout, "no results", GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH );
196 0 : pango_layout_set_width(font_layout, GUI_SKETCH_RESULT_LIST_PANGO_UNLIMITED_WIDTH );
197 0 : pango_cairo_show_layout( cr, font_layout );
198 :
199 : /* draw the icon */
200 0 : const int x = left+OBJ_GAP;
201 0 : const int y = top+OBJ_GAP;
202 0 : gui_sketch_texture_draw( (*this_).texture_downloader, undef_icon, x, y, cr );
203 : }
204 : else
205 : {
206 0 : for ( unsigned int idx = 0; idx < count; idx ++ )
207 : {
208 0 : const pos_search_result_t *const element = &((*this_).element_pos[idx]);
209 0 : gui_sketch_result_list_private_draw_element( this_, element, marker, font_layout, cr );
210 : }
211 : }
212 :
213 0 : g_object_unref(font_layout);
214 : }
215 :
216 0 : U8_TRACE_END();
217 0 : }
218 :
219 0 : void gui_sketch_result_list_private_draw_element( gui_sketch_result_list_t *this_,
220 : const pos_search_result_t *element,
221 : const gui_marked_set_t *marker,
222 : PangoLayout *font_layout,
223 : cairo_t *cr )
224 : {
225 0 : U8_TRACE_BEGIN();
226 0 : assert( NULL != cr );
227 0 : assert( NULL != element );
228 0 : assert( NULL != marker );
229 0 : assert( NULL != font_layout );
230 :
231 0 : const data_search_result_t *const result = pos_search_result_get_data_const( element );
232 :
233 : /* draw marker and set color */
234 : {
235 : shape_int_rectangle_t destination_rect;
236 0 : shape_int_rectangle_init_by_bounds( &destination_rect,
237 : pos_search_result_get_icon_box_const(element),
238 : pos_search_result_get_label_box_const(element)
239 : );
240 :
241 0 : const data_id_t highlighted = gui_marked_set_get_highlighted( marker );
242 0 : gui_sketch_marker_prepare_draw( &((*this_).sketch_marker),
243 : data_search_result_get_match_id( result ),
244 : marker,
245 : destination_rect,
246 : cr
247 : );
248 0 : if ( data_id_equals( &highlighted, data_search_result_get_diagram_id_const( result ) ) )
249 : {
250 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
251 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
252 : }
253 :
254 0 : shape_int_rectangle_destroy( &destination_rect );
255 : }
256 :
257 : /* draw text first, use the above set color and font */
258 : {
259 : /* what to draw */
260 0 : const char *const label = data_search_result_get_match_name_const( result );
261 :
262 : /* where to draw to */
263 : const shape_int_rectangle_t *const label_box
264 0 : = pos_search_result_get_label_box_const( element );
265 :
266 : /* do draw */
267 0 : cairo_move_to( cr, shape_int_rectangle_get_left(label_box), shape_int_rectangle_get_top(label_box) );
268 0 : pango_layout_set_text( font_layout, label, GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH );
269 0 : const unsigned int text_width
270 0 : = shape_int_rectangle_get_width(label_box)
271 0 : +(2.0*OBJ_GAP); /* add gap to avoid line breaks by rounding errors and whitespace character widths */
272 0 : pango_layout_set_width(font_layout, text_width * PANGO_SCALE );
273 0 : pango_cairo_show_layout( cr, font_layout );
274 : }
275 :
276 : /* draw the icon */
277 : {
278 : /* what to draw */
279 0 : const data_type_t result_type = data_search_result_get_match_type( result );
280 : gui_type_resource_t *const type_data
281 0 : = gui_type_resource_list_get_type ( &((*this_).selector), result_type );
282 0 : GdkTexture *const icon = gui_type_resource_get_icon( type_data );
283 :
284 : /* where to draw to */
285 : const shape_int_rectangle_t *const icon_box
286 0 : = pos_search_result_get_icon_box_const( element );
287 0 : const int x = shape_int_rectangle_get_left(icon_box);
288 0 : const int y = shape_int_rectangle_get_top(icon_box);
289 : /* double icon_width = gdk_texture_get_width ( icon ); */
290 : /* double icon_height = gdk_texture_get_width ( icon ); */
291 :
292 : /* do draw */
293 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, x, y, cr );
294 : }
295 :
296 0 : U8_TRACE_END();
297 0 : }
298 :
299 :
300 : /*
301 : Copyright 2018-2024 Andreas Warnke
302 :
303 : Licensed under the Apache License, Version 2.0 (the "License");
304 : you may not use this file except in compliance with the License.
305 : You may obtain a copy of the License at
306 :
307 : http://www.apache.org/licenses/LICENSE-2.0
308 :
309 : Unless required by applicable law or agreed to in writing, software
310 : distributed under the License is distributed on an "AS IS" BASIS,
311 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
312 : See the License for the specific language governing permissions and
313 : limitations under the License.
314 : */
|