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 "gui_sketch_int_compare.h"
7 : #include "u8/u8_trace.h"
8 : #include "u8/u8_log.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_table_t result_table = data_search_result_get_match_table( result );
104 0 : const int result_type = data_search_result_get_match_type( result );
105 : gui_type_resource_t *const type_data
106 0 : = gui_type_resource_list_get_type ( &((*this_).selector), result_table, result_type );
107 0 : GdkTexture *const icon = gui_type_resource_get_icon( type_data );
108 0 : const double icon_width = gdk_texture_get_width( icon );
109 0 : const double icon_height = gdk_texture_get_height( icon );
110 :
111 0 : const shape_int_rectangle_t new_icon_box = (shape_int_rectangle_t) {
112 0 : .left=left+OBJ_GAP,
113 0 : .top=(*io_y_pos)+OBJ_GAP,
114 0 : .width=icon_width+0.999,
115 0 : .height=icon_height+0.999 };
116 0 : pos_search_result_set_icon_box( element, &new_icon_box );
117 : }
118 :
119 : /* determine label dimensions */
120 0 : const shape_int_rectangle_t *const icon_box = pos_search_result_get_icon_box_const( element );
121 : {
122 0 : int_fast32_t proposed_pango_width = width - shape_int_rectangle_get_width(icon_box) - (4*OBJ_GAP);
123 0 : pango_layout_set_text( font_layout,
124 : data_search_result_get_match_name_const( result ),
125 : GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH
126 : );
127 0 : pango_layout_set_width(font_layout, proposed_pango_width * PANGO_SCALE );
128 : int text_width;
129 : int text_height;
130 0 : pango_layout_get_pixel_size(font_layout, &text_width, &text_height);
131 :
132 0 : int_fast32_t x_pos = shape_int_rectangle_get_right(icon_box);
133 :
134 0 : const shape_int_rectangle_t new_label_box = (shape_int_rectangle_t) {
135 0 : .left=x_pos+OBJ_GAP,
136 0 : .top=(*io_y_pos)+OBJ_GAP,
137 : .width=text_width,
138 : .height=text_height };
139 0 : pos_search_result_set_label_box( element, &new_label_box );
140 : }
141 :
142 0 : const shape_int_rectangle_t *const label_box = pos_search_result_get_label_box_const( element );
143 : *io_y_pos
144 0 : = gui_sketch_int_compare_max_i32( shape_int_rectangle_get_bottom(icon_box), shape_int_rectangle_get_bottom(label_box) )
145 0 : + OBJ_GAP;
146 :
147 0 : U8_TRACE_END();
148 0 : }
149 :
150 : static const double GREY_R = 0.8;
151 : static const double GREY_G = 0.8;
152 : static const double GREY_B = 0.8;
153 : static const double GREY_A = 1.0;
154 :
155 0 : void gui_sketch_result_list_draw ( gui_sketch_result_list_t *this_, const gui_marked_set_t *marker, cairo_t *cr )
156 : {
157 0 : U8_TRACE_BEGIN();
158 0 : assert( NULL != marker );
159 0 : assert( NULL != cr );
160 :
161 0 : if ( (*this_).visible )
162 : {
163 0 : PangoLayout *font_layout = pango_cairo_create_layout (cr);
164 : {
165 : const PangoFontDescription *const std_font
166 0 : = gui_sketch_style_get_standard_font_description( &((*this_).sketch_style ) );
167 0 : pango_layout_set_font_description ( font_layout, std_font );
168 : }
169 :
170 : /* draw background */
171 : {
172 0 : const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
173 0 : const int_fast32_t top = shape_int_rectangle_get_top( &((*this_).bounds) );
174 0 : const uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
175 0 : const uint_fast32_t height = shape_int_rectangle_get_height( &((*this_).bounds) );
176 :
177 0 : cairo_set_source_rgba( cr, GREY_R, GREY_G, GREY_B, GREY_A );
178 0 : cairo_rectangle ( cr, left, top, width, height );
179 0 : cairo_fill (cr);
180 : }
181 :
182 : /* draw icons and text */
183 0 : const unsigned int count = (*this_).element_count;
184 0 : assert( count <= GUI_SKETCH_RESULT_LIST_MAX_ELEMENTS );
185 0 : if ( count == 0 )
186 : {
187 0 : const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
188 0 : const int_fast32_t top = shape_int_rectangle_get_top( &((*this_).bounds) );
189 0 : GdkTexture *undef_icon = gui_resources_get_type_undef( (*this_).resources );
190 0 : double icon_width = gdk_texture_get_width ( undef_icon );
191 :
192 : /* draw text first, use the above set color and font */
193 0 : const GdkRGBA std_color = gui_sketch_style_get_standard_color( &((*this_).sketch_style) );
194 0 : cairo_set_source_rgba( cr, std_color.red, std_color.green, std_color.blue, std_color.alpha );
195 0 : cairo_move_to( cr, left+OBJ_GAP+icon_width, top+OBJ_GAP );
196 0 : pango_layout_set_text( font_layout, "no results", GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH );
197 0 : pango_layout_set_width(font_layout, GUI_SKETCH_RESULT_LIST_PANGO_UNLIMITED_WIDTH );
198 0 : pango_cairo_show_layout( cr, font_layout );
199 :
200 : /* draw the icon */
201 0 : const int x = left+OBJ_GAP;
202 0 : const int y = top+OBJ_GAP;
203 0 : gui_sketch_texture_draw( (*this_).texture_downloader, undef_icon, x, y, cr );
204 : }
205 : else
206 : {
207 0 : for ( unsigned int idx = 0; idx < count; idx ++ )
208 : {
209 0 : const pos_search_result_t *const element = &((*this_).element_pos[idx]);
210 0 : gui_sketch_result_list_private_draw_element( this_, element, marker, font_layout, cr );
211 : }
212 : }
213 :
214 0 : g_object_unref(font_layout);
215 : }
216 :
217 0 : U8_TRACE_END();
218 0 : }
219 :
220 0 : void gui_sketch_result_list_private_draw_element( gui_sketch_result_list_t *this_,
221 : const pos_search_result_t *element,
222 : const gui_marked_set_t *marker,
223 : PangoLayout *font_layout,
224 : cairo_t *cr )
225 : {
226 0 : U8_TRACE_BEGIN();
227 0 : assert( NULL != cr );
228 0 : assert( NULL != element );
229 0 : assert( NULL != marker );
230 0 : assert( NULL != font_layout );
231 :
232 0 : const data_search_result_t *const result = pos_search_result_get_data_const( element );
233 :
234 : /* draw marker and set color */
235 : {
236 : shape_int_rectangle_t destination_rect;
237 0 : shape_int_rectangle_init_by_bounds( &destination_rect,
238 : pos_search_result_get_icon_box_const(element),
239 : pos_search_result_get_label_box_const(element)
240 : );
241 :
242 0 : const data_id_t highlighted = gui_marked_set_get_highlighted( marker );
243 0 : gui_sketch_marker_prepare_draw( &((*this_).sketch_marker),
244 : data_search_result_get_match_id( result ),
245 : marker,
246 : destination_rect,
247 : cr
248 : );
249 0 : if ( data_id_equals( &highlighted, data_search_result_get_diagram_id_const( result ) ) )
250 : {
251 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
252 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
253 : }
254 :
255 0 : shape_int_rectangle_destroy( &destination_rect );
256 : }
257 :
258 : /* draw text first, use the above set color and font */
259 : {
260 : /* what to draw */
261 0 : const char *const label = data_search_result_get_match_name_const( result );
262 :
263 : /* where to draw to */
264 : const shape_int_rectangle_t *const label_box
265 0 : = pos_search_result_get_label_box_const( element );
266 :
267 : /* do draw */
268 0 : cairo_move_to( cr, shape_int_rectangle_get_left(label_box), shape_int_rectangle_get_top(label_box) );
269 0 : pango_layout_set_text( font_layout, label, GUI_SKETCH_RESULT_LIST_PANGO_AUTO_DETECT_LENGTH );
270 0 : const unsigned int text_width
271 0 : = shape_int_rectangle_get_width(label_box)
272 0 : +(2.0*OBJ_GAP); /* add gap to avoid line breaks by rounding errors and whitespace character widths */
273 0 : pango_layout_set_width(font_layout, text_width * PANGO_SCALE );
274 0 : pango_cairo_show_layout( cr, font_layout );
275 : }
276 :
277 : /* draw the icon */
278 : {
279 : /* what to draw */
280 0 : const data_table_t result_table = data_search_result_get_match_table( result );
281 0 : const int result_type = data_search_result_get_match_type( result );
282 : gui_type_resource_t *const type_data
283 0 : = gui_type_resource_list_get_type ( &((*this_).selector), result_table, result_type );
284 0 : GdkTexture *const icon = gui_type_resource_get_icon( type_data );
285 :
286 : /* where to draw to */
287 : const shape_int_rectangle_t *const icon_box
288 0 : = pos_search_result_get_icon_box_const( element );
289 0 : const int x = shape_int_rectangle_get_left(icon_box);
290 0 : const int y = shape_int_rectangle_get_top(icon_box);
291 : /* double icon_width = gdk_texture_get_width ( icon ); */
292 : /* double icon_height = gdk_texture_get_width ( icon ); */
293 :
294 : /* do draw */
295 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, x, y, cr );
296 : }
297 :
298 0 : U8_TRACE_END();
299 0 : }
300 :
301 :
302 : /*
303 : Copyright 2018-2024 Andreas Warnke
304 :
305 : Licensed under the Apache License, Version 2.0 (the "License");
306 : you may not use this file except in compliance with the License.
307 : You may obtain a copy of the License at
308 :
309 : http://www.apache.org/licenses/LICENSE-2.0
310 :
311 : Unless required by applicable law or agreed to in writing, software
312 : distributed under the License is distributed on an "AS IS" BASIS,
313 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
314 : See the License for the specific language governing permissions and
315 : limitations under the License.
316 : */
|