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