1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 17 июл. 2017 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 
24 namespace lsp
25 {
26     namespace tk
27     {
28         const w_class_t LSPGroup::metadata = { "LSPGroup", &LSPWidgetContainer::metadata };
29 
LSPGroup(LSPDisplay * dpy)30         LSPGroup::LSPGroup(LSPDisplay *dpy):
31             LSPWidgetContainer(dpy),
32             sText(this),
33             sColor(this),
34             sFont(this)
35         {
36             nRadius     = 10;
37             nBorder     = 0;
38             pWidget     = NULL;
39             bEmbed      = false;
40 
41             pClass      = &metadata;
42         }
43 
~LSPGroup()44         LSPGroup::~LSPGroup()
45         {
46             do_destroy();
47         }
48 
init()49         status_t LSPGroup::init()
50         {
51             sText.bind();
52 
53             status_t result = LSPWidgetContainer::init();
54             if (result != STATUS_OK)
55                 return result;
56 
57             if (pDisplay != NULL)
58             {
59                 LSPTheme *theme = pDisplay->theme();
60 
61                 if (theme != NULL)
62                 {
63                     sFont.init(theme->font());
64                     sFont.set_size(12.0f);
65                     init_color(C_BACKGROUND, sFont.color());
66                 }
67             }
68 
69             init_color(C_LABEL_TEXT, &sColor);
70 
71             return STATUS_OK;
72         }
73 
destroy()74         void LSPGroup::destroy()
75         {
76             do_destroy();
77             LSPWidgetContainer::destroy();
78         }
79 
find_widget(ssize_t x,ssize_t y)80         LSPWidget *LSPGroup::find_widget(ssize_t x, ssize_t y)
81         {
82             if (pWidget == NULL)
83                 return NULL;
84             return (pWidget->inside(x, y)) ? pWidget : NULL;
85         }
86 
query_dimensions(dimensions_t * d)87         void LSPGroup::query_dimensions(dimensions_t *d)
88         {
89             size_t bw       = (bEmbed) ? 1 : ::round(nRadius * M_SQRT2 * 0.5) + 1;
90             size_t dd       = bw + nBorder + 1;
91             d->nGapLeft     = dd;
92             d->nGapRight    = dd;
93             d->nGapTop      = dd;
94             d->nGapBottom   = dd;
95             d->nMinWidth    = nBorder*2;
96             d->nMinHeight   = nBorder*2;
97 
98             LSPString text;
99             sText.format(&text);
100             if (!text.is_empty())
101             {
102                 // Create temporary surface
103                 ISurface *s = (pDisplay != NULL) ? pDisplay->create_surface(1, 1) : NULL;
104                 if (s == NULL)
105                     return;
106 
107                 font_parameters_t   fp;
108                 text_parameters_t   tp;
109 
110                 sFont.get_parameters(s, &fp);
111                 sFont.get_text_parameters(s, &tp, &text);
112 
113                 d->nMinWidth    += tp.Width + nRadius * 3;
114                 d->nMinHeight   += fp.Height + nRadius * 2;
115                 d->nGapTop      += fp.Height;
116 
117                 // Destroy surface
118                 s->destroy();
119                 delete s;
120             }
121         }
122 
do_destroy()123         void LSPGroup::do_destroy()
124         {
125             if (pWidget != NULL)
126             {
127                 unlink_widget(pWidget);
128                 pWidget  = NULL;
129             }
130         }
131 
set_radius(size_t value)132         void LSPGroup::set_radius(size_t value)
133         {
134             if (nRadius == value)
135                 return;
136             nRadius = value;
137             query_resize();
138         }
139 
set_border(size_t value)140         void LSPGroup::set_border(size_t value)
141         {
142             if (nBorder == value)
143                 return;
144             nBorder = value;
145             query_resize();
146         }
147 
set_embed(bool embed)148         void LSPGroup::set_embed(bool embed)
149         {
150             if (bEmbed == embed)
151                 return;
152             bEmbed = embed;
153             query_resize();
154         }
155 
render(ISurface * s,bool force)156         void LSPGroup::render(ISurface *s, bool force)
157         {
158             if (nFlags & REDRAW_SURFACE)
159                 force = true;
160 
161             // Prepare palette
162             Color bg_color(sBgColor);
163             Color color(sColor);
164             color.scale_lightness(brightness());
165 
166 //            lsp_trace("Rendering this=%p, force=%d", this, int(force));
167             // Draw child
168             if (pWidget != NULL)
169             {
170                 if ((force) || (pWidget->redraw_pending()))
171                 {
172                     pWidget->render(s, force);
173                     pWidget->commit_redraw();
174                 }
175             }
176 
177             if (force)
178             {
179                 // Get resource
180                 ssize_t cx  = sSize.nLeft + nBorder + 1;
181                 ssize_t cy  = sSize.nTop + nBorder + 1;
182                 ssize_t sx  = sSize.nWidth - (nBorder << 1) - 1;
183                 ssize_t sy  = sSize.nHeight - (nBorder << 1) - 1;
184 //                size_t bw   = round(nRadius * M_SQRT2 * 0.5f) + 1;
185 
186                 // Draw background
187                 if (pWidget == NULL)
188                     s->fill_rect(sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight, bg_color);
189                 else
190                 {
191                     realize_t r;
192                     pWidget->get_dimensions(&r);
193 //                    Color red(1.0f, 0.0f, 0.0f);
194                     if ((bEmbed) && (nRadius > 1))
195                         s->fill_round_frame(
196                             sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight,
197                             r.nLeft, r.nTop, r.nWidth, r.nHeight,
198                             nRadius-1, SURFMASK_B_CORNER,
199                             bg_color
200                         );
201                     else
202                         s->fill_frame(
203                                 sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight,
204                                 r.nLeft, r.nTop, r.nWidth, r.nHeight,
205                                 bg_color
206                             );
207                 }
208 
209                 // Draw frame
210                 bool aa = s->set_antialiasing(true);
211                 s->wire_round_rect(cx, cy, sx-1, sy-1, nRadius, 0x0e, 2.0f, color);
212 
213                 // Draw text frame
214                 LSPString text;
215                 sText.format(&text);
216                 if (!text.is_empty())
217                 {
218                     // Draw text border
219                     font_parameters_t   fp;
220                     text_parameters_t   tp;
221 
222                     sFont.get_parameters(s, &fp);
223                     sFont.get_text_parameters(s, &tp, &text);
224 
225                     s->fill_round_rect(cx-1, cy-1, 4 + nRadius + tp.Width, fp.Height + 4, nRadius, 0x04, color);
226 
227                     // Show text
228                     Color font(sFont.raw_color());
229                     font.scale_lightness(brightness());
230                     sFont.draw(s, cx + 4, cy + fp.Ascent + nBorder, font, &text);
231                 }
232 
233                 s->set_antialiasing(aa);
234             }
235         }
236 
add(LSPWidget * widget)237         status_t LSPGroup::add(LSPWidget *widget)
238         {
239             if (pWidget != NULL)
240                 return STATUS_ALREADY_EXISTS;
241 
242             widget->set_parent(this);
243             pWidget = widget;
244             query_resize();
245             return STATUS_OK;
246         }
247 
remove(LSPWidget * widget)248         status_t LSPGroup::remove(LSPWidget *widget)
249         {
250             if (pWidget != widget)
251                 return STATUS_NOT_FOUND;
252 
253             unlink_widget(pWidget);
254             pWidget  = NULL;
255 
256             return STATUS_OK;
257         }
258 
size_request(size_request_t * r)259         void LSPGroup::size_request(size_request_t *r)
260         {
261             if (pWidget != NULL)
262                 pWidget->size_request(r);
263 
264             if (r->nMinWidth < 0)
265                 r->nMinWidth    = 0;
266             if (r->nMinHeight < 0)
267                 r->nMinHeight   = 0;
268 
269             if (pWidget != NULL)
270             {
271                 r->nMinWidth   += pWidget->padding()->horizontal();
272                 r->nMinHeight  += pWidget->padding()->vertical();
273             }
274 
275             dimensions_t d;
276             query_dimensions(&d);
277 
278             if (r->nMinWidth >= 0)
279             {
280                 size_t  n = r->nMinWidth + d.nGapLeft + d.nGapRight;
281                 if (n < d.nMinWidth)
282                     r->nMinWidth    = d.nMinWidth;
283                 else
284                     r->nMinWidth    = n;
285             }
286             if (r->nMinHeight >= 0)
287             {
288                 size_t  n = r->nMinHeight + d.nGapTop + d.nGapBottom;
289                 if (n < d.nMinHeight)
290                     r->nMinHeight   = d.nMinHeight;
291                 else
292                     r->nMinHeight   = n;
293             }
294 
295             // Align to 8-pixel grid
296 //            r->nMinWidth    = ((r->nMinWidth  + 7) >> 3) << 3;
297 //            r->nMinHeight   = ((r->nMinHeight + 7) >> 3) << 3;
298 
299             if ((r->nMaxWidth >= 0) && (r->nMaxWidth < r->nMinWidth))
300                 r->nMaxWidth    = r->nMinWidth;
301             if ((r->nMaxHeight >= 0) && (r->nMaxHeight < r->nMinHeight))
302                 r->nMaxHeight   = r->nMinHeight;
303         }
304 
realize(const realize_t * r)305         void LSPGroup::realize(const realize_t *r)
306         {
307             LSPWidgetContainer::realize(r);
308             if (pWidget == NULL)
309                 return;
310 
311             dimensions_t d;
312             query_dimensions(&d);
313 
314             size_request_t sr;
315             pWidget->size_request(&sr);
316 
317             realize_t rc;
318             rc.nLeft    = r->nLeft   + d.nGapLeft  + pWidget->padding()->left();
319             rc.nTop     = r->nTop    + d.nGapTop   + pWidget->padding()->top();
320             rc.nWidth   = r->nWidth  - d.nGapLeft  - d.nGapRight   - pWidget->padding()->horizontal();
321             rc.nHeight  = r->nHeight - d.nGapTop   - d.nGapBottom  - pWidget->padding()->vertical();
322 
323             if ((sr.nMaxWidth > 0) && (sr.nMaxWidth < rc.nWidth))
324             {
325                 rc.nLeft   += (rc.nWidth - sr.nMaxWidth) >> 1;
326                 rc.nWidth   = sr.nMaxWidth;
327             }
328 
329             if ((sr.nMaxHeight > 0) && (sr.nMaxHeight < rc.nHeight))
330             {
331                 rc.nTop    += (rc.nHeight - sr.nMaxHeight) >> 1;
332                 rc.nHeight  = sr.nMaxHeight;
333             }
334 
335             pWidget->realize(&rc);
336         }
337 
338     } /* namespace tk */
339 } /* namespace lsp */
340