• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gansm / finalcut / #767

09 Feb 2026 11:49PM UTC coverage: 69.07%. Remained the same
#767

push

travis-ci

gansm
More iterators and further code improvements

126 of 129 new or added lines in 7 files covered. (97.67%)

161 existing lines in 3 files now uncovered.

37625 of 54474 relevant lines covered (69.07%)

250.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.91
/final/fwidget_functions.cpp
1
/***********************************************************************
2
* fwidget_functions.cpp - FWidget helper functions                     *
3
*                                                                      *
4
* This file is part of the FINAL CUT widget toolkit                    *
5
*                                                                      *
6
* Copyright 2019-2026 Markus Gans                                      *
7
*                                                                      *
8
* FINAL CUT is free software; you can redistribute it and/or modify    *
9
* it under the terms of the GNU Lesser General Public License as       *
10
* published by the Free Software Foundation; either version 3 of       *
11
* the License, or (at your option) any later version.                  *
12
*                                                                      *
13
* FINAL CUT is distributed in the hope that it will be useful, but     *
14
* 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     *
19
* License along with this program.  If not, see                        *
20
* <http://www.gnu.org/licenses/>.                                      *
21
***********************************************************************/
22

23
#include <algorithm>
24
#include <array>
25
#include <memory>
26

27
#include "final/dialog/fdialog.h"
28
#include "final/fapplication.h"
29
#include "final/fevent.h"
30
#include "final/fwidgetcolors.h"
31
#include "final/fwidget_functions.h"
32
#include "final/fwidget.h"
33
#include "final/vterm/fcolorpair.h"
34
#include "final/vterm/fstyle.h"
35
#include "final/widget/fstatusbar.h"
36

37
namespace finalcut
38
{
39

40
namespace internal
41
{
42

43
constexpr auto getPrintTransMask() noexcept -> uInt32
44
{
45
  FCharAttribute mask{};
46
  mask.transparent = true;
47
  mask.color_overlay = true;
48
  mask.inherit_background = true;
49
  mask.no_changes = true;
50
  mask.printed = true;
51
  return FCharAttribute_to_uInt32(mask);
52
}
53

54
struct var
55
{
56
  static constexpr auto print_trans_mask = getPrintTransMask();
57
};
58

59
constexpr uInt32 var::print_trans_mask;
60

61
}  // namespace internal
62

63
//----------------------------------------------------------------------
64
struct TransparentShadowData
65
{
66
  // Using-declaration
67
  using FTermArea = FVTerm::FTermArea;
68

69
  // Data members
70
  FTermArea& area;
71
  uInt       width{};
72
  uInt       height{};
73
  uInt       shadow_width{};
74
  uInt       shadow_height{};
75
  FChar      transparent_char{};
76
  FChar      color_overlay_char{};
77
  FChar*     area_ptr{};
78
};
79

80

81
// Function forward declarations
82
//----------------------------------------------------------------------
83
void drawRightShadow (TransparentShadowData&);
84
void drawBottomShadow (TransparentShadowData&);
85

86

87
// FWidget non-member functions
88
//----------------------------------------------------------------------
89
auto isFocusNextKey (const FKey key) noexcept -> bool
×
90
{
91
  return ( key == FKey::Tab
92
        || key == FKey::Right
×
93
        || key == FKey::Down );
×
94
}
95

96
//----------------------------------------------------------------------
97
auto isFocusPrevKey (const FKey key) noexcept -> bool
×
98
{
99
  return ( key == FKey::Back_tab
100
        || key == FKey::Left
×
101
        || key == FKey::Up );
×
102
}
103

104
//----------------------------------------------------------------------
105
auto isDialogMenuKey (const FKey key) noexcept -> bool
×
106
{
107
  return ( key == FKey::Ctrl_caret     // Ctrl+^ (Ctrl+6)
108
        || key == FKey::F22            // Shift+F10
×
109
        || key == FKey::Shift_menu );  // Shift+Menu
×
110
}
111

112
//----------------------------------------------------------------------
113
auto isEnterKey (const FKey key) noexcept -> bool
×
114
{
115
  return ( key == FKey::Return
116
        || key == FKey::Enter );
×
117
}
118

119
//----------------------------------------------------------------------
120
auto isEscapeKey (const FKey key) noexcept -> bool
×
121
{
122
  return ( key == FKey::Escape
123
        || key == FKey::Escape_mintty );
×
124
}
125

126
//----------------------------------------------------------------------
127
auto isExpandComboBoxKey (const FKey key) noexcept -> bool
×
128
{
129
  return ( key == FKey::F4
130
        || key == FKey::Meta_down
×
131
        || key == FKey::Ctrl_down );
×
132
}
133

134
//----------------------------------------------------------------------
135
auto isCollapseComboBoxKey (const FKey key) noexcept -> bool
×
136
{
137
  return ( key == FKey::Meta_up
138
        || key == FKey::Ctrl_up
×
139
        || isEscapeKey(key) );
×
140
}
141

142
//----------------------------------------------------------------------
143
auto getFirstFocusableWidget (const FObject::FObjectList& list) -> FWidget*
×
144
{
145
  if ( list.empty() )
×
146
    return nullptr;
×
147

148
  auto iter = list.cbegin();
×
149

150
  while ( iter != list.cend() )
×
151
  {
152
    if ( (*iter)->isWidget() )
×
153
    {
154
      auto child = static_cast<FWidget*>(*iter);
×
155

156
      if ( child->isEnabled() && child->acceptFocus() )
×
157
        return child;
×
158
    }
159

160
    ++iter;
×
161
  }
162

163
  return nullptr;
×
164
}
165

166
//----------------------------------------------------------------------
167
auto getLastFocusableWidget (const FObject::FObjectList& list) -> FWidget*
×
168
{
169
  if ( list.empty() )
×
170
    return nullptr;
×
171

172
  auto iter  = list.cend();
×
173

174
  do
175
  {
176
    --iter;
×
177

178
    if ( ! (*iter)->isWidget() )
×
179
      continue;
×
180

181
    auto child = static_cast<FWidget*>(*iter);
×
182

183
    if ( child->isEnabled() && child->acceptFocus() )
×
184
      return child;
×
185
  }
186
  while ( iter != list.cbegin() );
×
187

188
  return nullptr;
×
189
}
190

191
//----------------------------------------------------------------------
192
auto isInFWidgetList (const FWidget::FWidgetList* list, const FWidget* obj) -> bool
2✔
193
{
194
  if ( ! list || ! obj )
2✔
195
    return false;
×
196

197
  return std::any_of ( list->cbegin(), list->cend()
2✔
198
                     , [&obj] (const auto& w) { return w == obj; } );
3✔
199
}
200

201
//----------------------------------------------------------------------
202
auto getFApplication() noexcept -> FApplication*
×
203
{
204
  return FApplication::getApplicationObject();
×
205
}
206

207
//----------------------------------------------------------------------
208
auto getAlignOffset ( Align alignment
×
209
                    , const std::size_t width
210
                    , const std::size_t length ) noexcept -> std::size_t
211
{
212
  if ( alignment == Align::Center )
×
213
  {
214
    if ( length < width )
×
215
      return (width - length) / 2;
×
216
  }
217
  else if ( alignment == Align::Right && length < width )
×
218
  {
219
    return width - length;
×
220
  }
221

222
  return 0;
×
223
}
224

225
//----------------------------------------------------------------------
226
auto getHotkey (const FString& text) -> FKey
×
227
{
228
  // Returns the hotkey character from a string
229
  // e.g. "E&xit" returns 'x'
230

231
  if ( text.isEmpty() )
×
232
    return FKey::None;
×
233

234
  std::size_t i{0};
×
235
  const std::size_t length = text.getLength();
×
236

237
  while ( i < length )
×
238
  {
239
    try
240
    {
241
      if ( i + 1 < length && text[i] == '&' )
×
242
      {
243
        i++;
×
244
        return FKey(text[i]);
×
245
      }
246
    }
247
    catch (const std::out_of_range&)
×
248
    {
249
      return FKey::None;
×
250
    }
×
251

252
    i++;
×
253
  }
254

255
  return FKey::None;
×
256
}
257

258
//----------------------------------------------------------------------
259
auto getHotkeyPos (const FString& src, FString& dest) -> std::size_t
×
260
{
261
  // Find hotkey position in string
262
  // + generate a new string without the '&'-sign
263

264
  static constexpr auto NOT_SET = static_cast<std::size_t>(-1);
265
  std::size_t hotkeypos{NOT_SET};
×
266
  std::size_t i{0};
×
267

268
  for (auto&& ch : src)
×
269
  {
270
    if ( ch == L'&' && hotkeypos == NOT_SET && src.getLength() != i + 1 )
×
271
      hotkeypos = i;
×
272
    else
273
      dest += ch;
×
274

275
    i++;
×
276
  }
277

278
  return hotkeypos;
×
279
}
280
//----------------------------------------------------------------------
281
void setHotkeyViaString (FWidget* w, const FString& text)
×
282
{
283
  // Set hotkey accelerator via string
284

285
  if ( ! w )
×
286
    return;
×
287

288
  FKey hotkey = getHotkey(text);
×
289

290
  if ( hotkey > 0xff00 && hotkey < 0xff5f )  // full-width character
×
291
    hotkey -= 0xfee0;
×
292

293
  if ( hotkey != FKey::None )
×
294
  {
295
    if ( std::isalpha(int(hotkey)) || std::isdigit(int(hotkey)) )
×
296
    {
297
      w->addAccelerator (FKey(std::tolower(int(hotkey))));
×
298
      w->addAccelerator (FKey(std::toupper(int(hotkey))));
×
299
      // Meta + hotkey
300
      w->addAccelerator (FKey::Meta_offset + FKey(std::tolower(int(hotkey))));
×
301
    }
302
    else
303
      w->addAccelerator (hotkey);
×
304
  }
305
  else
306
    w->delAccelerator();
×
307
}
308

309
//----------------------------------------------------------------------
310
void setWidgetFocus (FWidget* widget)
×
311
{
312
  if ( ! widget || widget->hasFocus() )
×
313
    return;
×
314

315
  auto focused_widget = FWidget::getFocusWidget();
×
316
  widget->setFocus();
×
317

318
  if ( focused_widget && focused_widget->isWidget() )  // old focused widget
×
319
    focused_widget->redraw();
×
320

321
  widget->redraw();
×
322
  drawStatusBarMessage();
×
323
}
324

325
//----------------------------------------------------------------------
326
void setWidgetShadow (FWidget* w, bool enable)
×
327
{
328
  if ( enable
×
329
    && FVTerm::getFOutput()->getEncoding() != Encoding::VT100
×
330
    && FVTerm::getFOutput()->getEncoding() != Encoding::ASCII )
×
331
  {
332
    w->setFlags().shadow.shadow = true;
×
333
    w->setShadowSize(FSize{1, 1});
×
334
  }
335
  else
336
  {
337
    w->setFlags().shadow.shadow = false;
×
338
    w->setShadowSize(FSize{0, 0});
×
339
  }
340
}
×
341

342
//----------------------------------------------------------------------
343
void passResizeCornerEventToDialog (const FWidget* w, const FMouseEvent& ev)
×
344
{
345
  // Pass mouse event to the parent widget
346

347
  auto parent = w->getParentWidget();
×
348

349
  if ( ! parent || ! parent->isDialogWidget() )
×
350
    return;
×
351

352
  const auto& dialog = static_cast<FDialog*>(parent);
×
353

354
  if ( ! dialog->isResizeable() || dialog->isZoomed() )
×
355
    return;
×
356

357
  const auto& type = ev.getType();
×
358
  const auto& tpos = ev.getTermPos();
×
359
  const auto& par = parent->termToWidgetPos(tpos);
×
360
  const auto btn = ev.getButton();
×
361
  const auto& new_ev = \
362
      std::make_shared<FMouseEvent>(type, par, tpos, btn);
×
363
  FApplication::sendEvent (parent, new_ev.get());
×
364
}
×
365

366
//----------------------------------------------------------------------
367
void drawShadow (FWidget* w)
×
368
{
369
  if ( FVTerm::getFOutput()->isMonochron() && ! w->flags.shadow.trans_shadow )
×
370
    return;
×
371

372
  if ( (FVTerm::getFOutput()->getEncoding() == Encoding::VT100 && ! w->flags.shadow.trans_shadow)
×
373
    || (FVTerm::getFOutput()->getEncoding() == Encoding::ASCII && ! w->flags.shadow.trans_shadow) )
×
374
  {
375
    clearBlockShadow(w);
×
376
    return;
×
377
  }
378

379
  if ( w->flags.shadow.trans_shadow )
×
380
    drawTransparentShadow (w);  // transparent shadow
×
381
  else if ( w->flags.shadow.shadow )
×
382
    drawBlockShadow (w);        // non-transparent shadow
×
383
}
384

385
//----------------------------------------------------------------------
386
void drawTransparentShadow (FWidget* w)
×
387
{
388
  // transparent shadow
389

390
  if ( ! w || ! w->getPrintArea() )
×
391
    return;
×
392

393
  auto& area = *w->getPrintArea();
×
394
  const auto& wc_shadow = FWidget::getColorTheme()->shadow;
×
395

396
  TransparentShadowData data
×
397
  {
398
    area,
399
    uInt(area.size.width),
×
400
    uInt(area.size.height),
×
401
    uInt(area.shadow.width),
×
402
    uInt(area.shadow.height),
×
403
    {
404
      { L'\0',  L'\0', L'\0', L'\0', L'\0' },
405
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
406
      { { FColor::Default, FColor::Default } },
407
      { { 0x00, 0x20, 0x00, 0x00} }  // byte 0..3 (byte 1 = 0x32 = transparent)
408
    },
409
    {
410
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
411
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
412
      { { wc_shadow.fg, wc_shadow.bg } },
×
413
      { { 0x00, 0x40, 0x00, 0x00} }  // byte 0..3 (byte 1 = 0x64 = color_overlay)
414
    },
415
    &area.getFChar(area.size.width, 0)
×
416
  };
×
417

418
  drawRightShadow(data);
×
419
  drawBottomShadow(data);
×
420
  area.has_changes = true;
×
421
  // Update row changes
422
  auto last_row = uInt(data.height + data.shadow_height - 1);
×
423
  area.changes_in_row = {uInt(0), last_row};
×
424

425
  if ( FVTerm::getFOutput()->isMonochron() )
×
426
    w->setReverse(false);
×
427
}
428

429
//----------------------------------------------------------------------
430
inline void drawRightShadow (TransparentShadowData& d)
×
431
{
432
  if ( d.shadow_width > 0 )  // Draw right shadow
×
433
  {
434
    std::fill (d.area_ptr, d.area_ptr + d.shadow_width, d.transparent_char);
×
435
    d.area.changes_in_line[0].xmin = std::min(d.area.changes_in_line[0].xmin, d.width);
×
436
    d.area.changes_in_line[0].xmax = d.width + d.shadow_width - 1;
×
437
    d.area.changes_in_line[0].trans_count += d.shadow_width;
×
438

439
    for (std::size_t y{1}; y < d.height; y++)
×
440
    {
441
      d.area_ptr += d.shadow_width + d.width;
×
442
      d.area.changes_in_line[y].xmin = std::min(d.area.changes_in_line[y].xmin, d.width);
×
443
      d.area.changes_in_line[y].xmax = d.width + d.shadow_width - 1;
×
444
      d.area.changes_in_line[y].trans_count += d.shadow_width;
×
445
      std::fill (d.area_ptr, d.area_ptr + d.shadow_width, d.color_overlay_char);
×
446
    }
447

448
    d.area_ptr += d.shadow_width;
×
449
  }
450
}
×
451

452
//----------------------------------------------------------------------
453
inline void drawBottomShadow (TransparentShadowData& d)
×
454
{
455
  for (std::size_t y{d.height}; y < d.height + d.shadow_height; y++)  // Draw bottom shadow
×
456
  {
457
    d.area.changes_in_line[y].xmin = 0;
×
458
    d.area.changes_in_line[y].xmax = d.width + d.shadow_width - 1;
×
459
    d.area.changes_in_line[y].trans_count += d.width + d.shadow_width;
×
460
    std::fill (d.area_ptr, d.area_ptr + d.shadow_width, d.transparent_char);
×
461
    d.area_ptr += d.shadow_width;
×
462
    std::fill (d.area_ptr, d.area_ptr + d.width, d.color_overlay_char);
×
463
    d.area_ptr += d.width;
×
464
  }
465
}
×
466

467
//----------------------------------------------------------------------
468
void drawBlockShadow (FWidget* w)
×
469
{
470
  // non-transparent shadow
471

472
  if ( ! w || ! FVTerm::getFOutput()->hasShadowCharacter() )
×
473
    return;
×
474

475
  const auto& wc_shadow = FWidget::getColorTheme()->shadow;
×
476

477
  std::array<FChar, 4> shadow_char
×
478
  {{
479
    {
480
      { wchar_t(UniChar::LowerHalfBlock),  L'\0', L'\0', L'\0', L'\0' },  // ▄
481
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
482
      { { wc_shadow.bg, FColor::Default } },
×
483
      { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
484
    },
485
    {
486
      { wchar_t(UniChar::FullBlock),  L'\0', L'\0', L'\0', L'\0' },  // █
487
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
488
      { { wc_shadow.bg, FColor::Default } },
×
489
      { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
490
    },
491
    {
492
      { L' ',  L'\0', L'\0', L'\0', L'\0' },  // ' '
493
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
494
      { { FColor::Default, FColor::Default } },
495
      { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
496
    },
497
    {
498
      { wchar_t(UniChar::UpperHalfBlock),  L'\0', L'\0', L'\0', L'\0' },  // ▄
499
      { L'\0', L'\0', L'\0', L'\0', L'\0' },
500
      { { wc_shadow.bg, FColor::Default } },
×
501
      { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
502
    }
503
  }};
×
504

505
  if ( w->isWindowWidget() )
×
506
  {
507
    shadow_char[0].attr.bit.inherit_background = true;
×
508
    shadow_char[1].attr.bit.inherit_background = true;
×
509
    shadow_char[2].attr.bit.transparent = true;
×
510
    shadow_char[3].attr.bit.inherit_background = true;
×
511
  }
512
  else if ( auto p = w->getParentWidget() )
×
513
  {
514
    shadow_char[0].color.pair.bg = p->getBackgroundColor();
×
515
    shadow_char[1].color.pair.bg = p->getBackgroundColor();
×
516
    shadow_char[2].color.pair.bg = p->getBackgroundColor();
×
517
    shadow_char[3].color.pair.bg = p->getBackgroundColor();
×
518
  }
519

520
  drawGenericBlockShadow (w, shadow_char);
×
521
}
522

523
//----------------------------------------------------------------------
524
void clearBlockShadow (FWidget* w)
×
525
{
526
  if ( ! w || ! FVTerm::getFOutput()->hasShadowCharacter() )
×
527
    return;
×
528

529
  FChar spacer_char
×
530
  {
531
    { L' ',  L'\0', L'\0', L'\0', L'\0' },  // ' '
532
    { L'\0', L'\0', L'\0', L'\0', L'\0' },
533
    { { FColor::Default, FColor::Default } },
534
    { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
535
  };
536

537
  if ( w->isWindowWidget() )
×
538
    spacer_char.attr.bit.transparent = true;
×
539
  else if ( auto p = w->getParentWidget() )
×
540
    spacer_char.color.pair.bg = p->getBackgroundColor();
×
541

542
  const std::array<FChar, 4> shadow_char
543
  {
544
    {spacer_char, spacer_char, spacer_char, spacer_char}
545
  };
×
546

547
  drawGenericBlockShadow (w, shadow_char);
×
548
}
549

550
//----------------------------------------------------------------------
551
struct GenericBlockShadowData
552
{
553
  // Constructor
554
  GenericBlockShadowData (FWidget* w, const std::array<FChar, 4>& chars)
×
555
    : shadow_char{chars}
×
556
    , area{*w->getPrintArea()}
×
557
    , is_window{w->isWindowWidget()}
×
558
    , width{is_window ? uInt(area.size.width) : uInt(w->getWidth())}
×
559
    , height{is_window ? uInt(area.size.height) : uInt(w->getHeight())}
×
560
    , shadow_width{uInt(area.shadow.width)}
×
561
    , shadow_height{uInt(area.shadow.height)}
×
562
    , x_offset{uInt(w->woffset.getX1() + w->getX() - area.position.x - 1)}
×
563
    , y_offset{uInt(w->woffset.getY1() + w->getY() - area.position.y - 1)}
×
564
    , shadow_x{x_offset + width}
×
565
    , shadow_y{y_offset + height}
×
566
  { }
×
567

568
  inline void updateChanges (uInt y, uInt xmin, uInt xmax, uInt tc = 0)
×
569
  {
570
    auto& changes = area.changes_in_line[y];
×
571
    changes.xmin = std::min(changes.xmin, xmin);
×
572
    changes.xmax = std::max(changes.xmax, xmax);
×
573
    changes.trans_count += tc + 1;
×
574
  }
×
575

576
  // Data members
577
  const std::array<FChar, 4>& shadow_char{};
578
  FVTerm::FTermArea& area;
579
  const bool is_window{false};
580
  const uInt width{0};
581
  const uInt height{0};
582
  const uInt shadow_width{0};
583
  const uInt shadow_height{0};
584
  const uInt x_offset{0};
585
  const uInt y_offset{0};
586
  const uInt shadow_x{0};  // Shadow column (right)
587
  const uInt shadow_y{0};  // Shadow row (bottom)
588
};
589

590
//----------------------------------------------------------------------
591
void drawGenericBlockShadow ( FWidget* w
×
592
                            , const std::array<FChar, 4>& shadow_char )
593
{
594
  if ( ! w
×
595
    || ! w->getPrintArea()
×
596
    || ! FVTerm::getFOutput()->hasShadowCharacter() )
×
597
    return;
×
598

599
  GenericBlockShadowData bsd(w, shadow_char);
×
600

601
  if ( bsd.is_window && (bsd.shadow_width < 1 || bsd.shadow_height < 1) )
×
602
    return;
×
603

604
  // Draw the top-right shadow
605
  auto area_iter = bsd.area.getFCharIterator(int(bsd.shadow_x), int(bsd.y_offset));
×
606
  *area_iter = bsd.shadow_char[0];  // ▄ (top-right corner)
×
607
  bsd.updateChanges(bsd.y_offset, bsd.shadow_x, bsd.shadow_x);
×
608

609
  // Draw the right-side shadow
610
  for (uInt y = bsd.y_offset + 1; y < bsd.shadow_y; y++)
×
611
  {
612
    area_iter = bsd.area.getFCharIterator(int(bsd.shadow_x), int(y));
×
613
    *area_iter = bsd.shadow_char[1];  // █
×
614
    bsd.updateChanges(y, bsd.shadow_x, bsd.shadow_x);
×
615
  }
616

617
  // Draw the bottom shadow
618
  area_iter = bsd.area.getFCharIterator(int(bsd.x_offset), int(bsd.shadow_y));
×
619
  *area_iter = bsd.shadow_char[2];  // ' ' (bottom-left corner)
×
620
  ++area_iter;
×
621
  // Fill the horizontal shadow
622
  std::fill (area_iter, area_iter + bsd.width, bsd.shadow_char[3]);  // ▀
×
623
  bsd.updateChanges(bsd.shadow_y, bsd.x_offset, bsd.shadow_x, bsd.width);
×
624
  // Update row changes
625
  auto& changes_in_row = bsd.area.changes_in_row;
×
626
  changes_in_row.ymin = std::min(changes_in_row.ymin, bsd.y_offset);
×
627
  changes_in_row.ymax = std::max(changes_in_row.ymax, bsd.shadow_y);
×
628
}
629

630
//----------------------------------------------------------------------
631
inline void setWidgetColors (const FWidget* w)
×
632
{
633
  const auto& wc_dialog = FWidget::getColorTheme()->dialog;
×
634

635
  if ( auto p = w->getParentWidget() )
×
636
    w->setColor (wc_dialog.fg, p->getBackgroundColor());
×
637
  else
638
    w->setColor (wc_dialog.fg, wc_dialog.bg);
×
639
}
×
640

641
//----------------------------------------------------------------------
642
inline void drawVerticalFlatBorder (FWidget* w)
×
643
{
644
  const std::size_t width = w->getWidth();
×
645
  const std::size_t height = w->getHeight();
×
646

647
  for (std::size_t y{0}; y < height; y++)
×
648
  {
649
    w->print() << FPoint {0, int(y) + 1};
×
650

651
    if ( w->double_flatline_mask.left[uLong(y)] )
×
652
      // left+right line (on left side)
653
      w->print (UniChar::NF_rev_border_line_right_and_left);
×
654
    else
655
      // right line (on left side)
656
      w->print (UniChar::NF_rev_border_line_right);
×
657

658
    w->print() << FPoint {int(width) + 1, int(y) + 1};
×
659

660
    if ( w->double_flatline_mask.right[y] )
×
661
      // left+right line (on right side)
662
      w->print (UniChar::NF_rev_border_line_right_and_left);
×
663
    else
664
      // left line (on right side)
665
      w->print (UniChar::NF_border_line_left);
×
666
  }
667
}
×
668

669
//----------------------------------------------------------------------
670
inline void drawHorizontalFlatBorder (FWidget* w)
×
671
{
672
  const std::size_t width = w->getWidth();
×
673
  const std::size_t height = w->getHeight();
×
674
  w->print() << FPoint {1, 0};
×
675

676
  for (std::size_t x{0}; x < width; x++)
×
677
  {
678
    if ( w->double_flatline_mask.top[x] )
×
679
      // top+bottom line (at top)
680
      w->print (UniChar::NF_border_line_up_and_down);
×
681
    else
682
      // bottom line (at top)
683
      w->print (UniChar::NF_border_line_bottom);
×
684
  }
685

686
  w->print() << FPoint {1, int(height) + 1};
×
687

688
  for (std::size_t x{0}; x < width; x++)
×
689
  {
690
    if ( w->double_flatline_mask.bottom[x] )
×
691
      // top+bottom line (at bottom)
692
      w->print (UniChar::NF_border_line_up_and_down);
×
693
    else
694
      // top line (at bottom)
695
      w->print (UniChar::NF_border_line_upper);
×
696
  }
697
}
×
698

699
//----------------------------------------------------------------------
700
void drawFlatBorder (FWidget* w)
×
701
{
702
  if ( ! FVTerm::getFOutput()->isNewFont() )
×
703
    return;
×
704

705
  setWidgetColors(w);
×
706
  drawVerticalFlatBorder(w);
×
707
  drawHorizontalFlatBorder(w);
×
708
}
709

710
//----------------------------------------------------------------------
711
inline void clearVerticalFlatBorder (FWidget* w)
×
712
{
713
  const std::size_t width = w->getWidth();
×
714
  const std::size_t height = w->getHeight();
×
715

716
  for (std::size_t y{0}; y < height; y++)
×
717
  {
718
    // clear on left side
719
    w->print() << FPoint {0, int(y) + 1};
×
720

721
    if ( w->double_flatline_mask.left[y] )
×
722
      w->print (UniChar::NF_border_line_left);
×
723
    else
724
      w->print (' ');
×
725

726
    // clear on right side
727
    w->print() << FPoint {int(width) + 1, int(y) + 1};
×
728

729
    if ( w->double_flatline_mask.right[y] )
×
730
      w->print (UniChar::NF_rev_border_line_right);
×
731
    else
732
      w->print (' ');
×
733
  }
734
}
×
735

736
//----------------------------------------------------------------------
737
inline void clearHorizontalFlatBorder (FWidget* w)
×
738
{
739
  const std::size_t width = w->getWidth();
×
740
  const std::size_t height = w->getHeight();
×
741

742
  // clear at top
743
  w->print() << FPoint {1, 0};
×
744

745
  for (std::size_t x{0}; x < width; x++)
×
746
  {
747
    if ( w->double_flatline_mask.top[x] )
×
748
      w->print (UniChar::NF_border_line_upper);
×
749
    else
750
      w->print (' ');
×
751
  }
752

753
  // clear at bottom
754
  w->print() << FPoint {1, int(height) + 1};
×
755

756
  for (std::size_t x{0}; x < width; x++)
×
757
  {
758
    if ( w->double_flatline_mask.bottom[x] )
×
759
      w->print (UniChar::NF_border_line_bottom);
×
760
    else
761
      w->print (' ');
×
762
  }
763
}
×
764

765
//----------------------------------------------------------------------
766
void clearFlatBorder (FWidget* w)
×
767
{
768
  if ( ! FVTerm::getFOutput()->isNewFont() )
×
769
    return;
×
770

771
  setWidgetColors(w);
×
772
  clearVerticalFlatBorder(w);
×
773
  clearHorizontalFlatBorder(w);
×
774
}
775

776
//----------------------------------------------------------------------
777
inline void checkBorder (const FWidget* w, FRect& r)
×
778
{
779
  if ( r.x1_ref() > r.x2_ref() )
×
780
    std::swap (r.x1_ref(), r.x2_ref());
×
781

782
  if ( r.y1_ref() > r.y2_ref() )
×
783
    std::swap (r.y1_ref(), r.y2_ref());
×
784

785
  r.x1_ref() = std::max(r.x1_ref(), 1);
×
786
  r.y1_ref() = std::max(r.y1_ref(), 1);
×
787
  r.x2_ref() = std::min(r.x2_ref(), r.x1_ref() + int(w->getWidth()) - 1);
×
788
  r.y2_ref() = std::min(r.y2_ref(), r.y1_ref() + int(w->getHeight()) - 1);
×
789
}
×
790

791
//----------------------------------------------------------------------
792
void drawBorder (FWidget* w, const FRect& r)
×
793
{
794
  FRect rect = r;
×
795
  checkBorder (w, rect);
×
796

797
  if ( r.getWidth() < 3 )
×
798
    return;
×
799

800
  if ( FVTerm::getFOutput()->isNewFont() )
×
801
    drawNewFontBox (w, rect);
×
802
  else
803
    drawBox (w, rect);
×
804
}
805

806
//----------------------------------------------------------------------
807
void drawListBorder (FWidget* w, const FRect& r)
×
808
{
809
  FRect rect = r;
×
810
  checkBorder (w, rect);
×
811

812
  if ( r.getWidth() < 3 )
×
813
    return;
×
814

815
  if ( FVTerm::getFOutput()->isNewFont() )
×
816
    drawNewFontListBox (w, rect);
×
817
  else
818
    drawBox (w, rect);
×
819
}
820

821
//----------------------------------------------------------------------
822
inline void drawBox (FWidget* w, const FRect& r)
×
823
{
824
  // Use box-drawing characters to draw a border
825

826
  static constexpr std::array<wchar_t, 8> box_char
827
  {{
828
    static_cast<wchar_t>(UniChar::BoxDrawingsDownAndRight),  // ┌
829
    static_cast<wchar_t>(UniChar::BoxDrawingsHorizontal),    // ─
830
    static_cast<wchar_t>(UniChar::BoxDrawingsDownAndLeft),   // ┐
831
    static_cast<wchar_t>(UniChar::BoxDrawingsVertical),      // │
832
    static_cast<wchar_t>(UniChar::BoxDrawingsVertical),      // │
833
    static_cast<wchar_t>(UniChar::BoxDrawingsUpAndRight),    // └
834
    static_cast<wchar_t>(UniChar::BoxDrawingsHorizontal),    // ─
835
    static_cast<wchar_t>(UniChar::BoxDrawingsUpAndLeft)      // ┘
836
  }};
837

838
  drawGenericBox (w, r, box_char);
×
839
}
×
840

841
//----------------------------------------------------------------------
842
inline void drawNewFontBox (FWidget* w, const FRect& r)
×
843
{
844
  // Use new graphical font characters to draw a border
845

846
  static constexpr std::array<wchar_t, 8> box_char
847
  {{
848
    static_cast<wchar_t>(UniChar::NF_border_corner_middle_upper_left),   // ┌
849
    static_cast<wchar_t>(UniChar::NF_border_line_horizontal),            // ─
850
    static_cast<wchar_t>(UniChar::NF_border_corner_middle_upper_right),  // ┐
851
    static_cast<wchar_t>(UniChar::NF_border_line_vertical),              // │
852
    static_cast<wchar_t>(UniChar::NF_border_line_vertical),              // │
853
    static_cast<wchar_t>(UniChar::NF_border_corner_middle_lower_left),   // └
854
    static_cast<wchar_t>(UniChar::NF_border_line_horizontal),            // ─
855
    static_cast<wchar_t>(UniChar::NF_border_corner_middle_lower_right)   // ┘
856
  }};
857

858
  drawGenericBox (w, r, box_char);
×
859
}
×
860

861
//----------------------------------------------------------------------
862
void drawNewFontUShapedBox (FWidget* w, const FRect& r)
×
863
{
864
  // Draw a new graphical font U-shaped frame
865

866
  static constexpr std::array<wchar_t, 8> box_char
867
  {{
868
    static_cast<wchar_t>(UniChar::NF_border_line_left),              // border left ⎸
869
    L' ',                                                            // space ' '
870
    static_cast<wchar_t>(UniChar::NF_rev_border_line_right),         // border right⎹
871
    static_cast<wchar_t>(UniChar::NF_border_line_left),              // border left ⎸
872
    static_cast<wchar_t>(UniChar::NF_rev_border_line_right),         // border right⎹
873
    static_cast<wchar_t>(UniChar::NF_border_corner_lower_left),      // ⎣
874
    static_cast<wchar_t>(UniChar::NF_border_line_bottom),            // _
875
    static_cast<wchar_t>(UniChar::NF_rev_border_corner_lower_right)  // ⎦
876
  }};
877

878
  drawGenericBox (w, r, box_char);
×
879
}
×
880

881
//----------------------------------------------------------------------
882
inline void drawNewFontListBox (FWidget* w, const FRect& r)
×
883
{
884
  static constexpr std::array<wchar_t, 8> box_char
885
  {{
886
    static_cast<wchar_t>(UniChar::NF_border_line_middle_left_down),  // ┌
887
    static_cast<wchar_t>(UniChar::NF_border_line_horizontal),        // ─
888
    static_cast<wchar_t>(UniChar::NF_border_line_left_down),         // ╷
889
    static_cast<wchar_t>(UniChar::NF_border_line_left),              // border left ⎸
890
    static_cast<wchar_t>(UniChar::NF_border_line_left),              // border left ⎸
891
    static_cast<wchar_t>(UniChar::NF_border_line_middle_right_up),   // └
892
    static_cast<wchar_t>(UniChar::NF_border_line_horizontal),        // ─
893
    static_cast<wchar_t>(UniChar::NF_border_line_left_up)            // ╵
894
  }};
895

896
  drawGenericBox (w, r, box_char);
×
897
}
×
898

899
//----------------------------------------------------------------------
900
struct GenericBoxData
901
{
902
  // Constructor
903
  GenericBoxData ( FWidget* w, const FRect& r
×
904
                 , const std::array<wchar_t, 8>& chars )
905
    : box_char{chars}
×
906
    , area{*w->getPrintArea()}
×
907
    , x_offset{w->woffset.getX1() + w->getX() - area.position.x - 1}
×
908
    , y_offset{w->woffset.getY1() + w->getY() - area.position.y - 1}
×
909
    , x1{int(x_offset) + r.getX1() - 1}
×
910
    , y1{int(y_offset) + r.getY1() - 1}
×
911
    , x2{int(x_offset) + r.getX2() - 1}
×
912
    , y2{int(y_offset) + r.getY2() - 1}
×
913
    , max_width{uInt(area.size.width + area.shadow.width - 1)}
×
914
    , width{uInt(r.getWidth())}
×
915
    , line_length{width - 2}
×
NEW
916
    , is_transparent{(fchar.attr.byte[1] & internal::var::print_trans_mask) != 0}
×
917
    , trans_count_increment{uInt(is_transparent) * width}
×
918
  {
919
    // Prepare the first character to draw the box
920
    fchar.attr.bit.char_width = 1;
×
921
    fchar.ch[1] = L'\0';
×
922
  }
×
923

924
  inline void updateChanges (uInt y, uInt xmin, uInt xmax)
×
925
  {
926
    auto& changes = area.changes_in_line[y];
×
927
    changes.xmin = std::min(changes.xmin, xmin);
×
928
    changes.xmax = std::min(max_width, std::max(changes.xmax, xmax));
×
929
    changes.trans_count += trans_count_increment;
×
930
  }
×
931

932
  // Data members
933
  const std::array<wchar_t, 8>& box_char{};
934
  FChar& fchar{FVTermAttribute::getAttribute()};
935
  FVTerm::FTermArea& area;
936
  const int  x_offset{0};
937
  const int  y_offset{0};
938
  const int  x1{0};
939
  const int  y1{0};
940
  const int  x2{-1};
941
  const int  y2{-1};
942
  const uInt max_width{0};
943
  const uInt width{0};
944
  const uInt line_length{0};
945
  const bool is_transparent{false};
946
  const uInt trans_count_increment{0};
947
};
948

949
//----------------------------------------------------------------------
950
inline void drawBoxTopLine (GenericBoxData& bd)
×
951
{
952
  // Draw the horizontal top line of the box
953

954
  auto area_iter = bd.area.getFCharIterator(bd.x1, bd.y1);
×
955

956
  bd.fchar.ch[0] = bd.box_char[0];  // Upper left corner
×
957
  *area_iter = bd.fchar;
×
958

959
  bd.fchar.ch[0] = bd.box_char[1];  // Upper line
×
960
  area_iter = std::fill_n(area_iter + 1, bd.line_length, bd.fchar);
×
961

962
  bd.fchar.ch[0] = bd.box_char[2];  // Upper right corner
×
963
  *area_iter = bd.fchar;
×
964

965
  // Update area_changes for the top line
966
  bd.updateChanges(uInt(bd.y1), uInt(bd.x1), uInt(bd.x2));
×
967
}
×
968

969
//----------------------------------------------------------------------
970
inline void drawBoxSides (GenericBoxData& bd)
×
971
{
972
  // Draw the vertical sides of the box
973

974
  for (auto y = bd.y1 + 1; y < bd.y2; y++)
×
975
  {
976
    auto left_iter = bd.area.getFCharIterator(bd.x1, y);
×
977
    auto right_iter = left_iter + bd.width - 1;
×
978

979
    bd.fchar.ch[0] = bd.box_char[3];  // Left line
×
980
    *left_iter = bd.fchar;
×
981

982
    bd.fchar.ch[0] = bd.box_char[4];  // Right line
×
983
    *right_iter = bd.fchar;
×
984

985
    // Update area_changes for the sides
986
    bd.updateChanges(uInt(y), uInt(bd.x1), uInt(bd.x2));
×
987
  }
988
}
×
989

990
//----------------------------------------------------------------------
991
inline void drawBoxBottomLine (GenericBoxData& bd)
×
992
{
993
  // Draw the horizontal bottom line of the box
994

995
  auto area_iter = bd.area.getFCharIterator(bd.x1, bd.y2);
×
996

997
  bd.fchar.ch[0] = bd.box_char[5];  // Upper left corner
×
998
  *area_iter = bd.fchar;
×
999

1000
  bd.fchar.ch[0] = bd.box_char[6];  // Bottom line
×
1001
  area_iter = std::fill_n(area_iter + 1, bd.line_length, bd.fchar);
×
1002

1003
  bd.fchar.ch[0] = bd.box_char[7];  // Bottom left corner
×
1004
  *area_iter = bd.fchar;
×
1005

1006
  // Update area_changes for the bottom line
1007
  bd.updateChanges(uInt(bd.y2), uInt(bd.x1), uInt(bd.x2));
×
1008
}
×
1009

1010
//----------------------------------------------------------------------
1011
void drawGenericBox ( FWidget* w, const FRect& r
×
1012
                    , const std::array<wchar_t, 8>& box_char )
1013
{
1014
  if ( ! w || ! w->getPrintArea() || r.getWidth() < 3 )
×
1015
    return;
×
1016

1017
  GenericBoxData box_data(w, r, box_char);
×
1018

1019
  drawBoxTopLine (box_data);
×
1020
  drawBoxSides (box_data);
×
1021
  drawBoxBottomLine (box_data);
×
1022
  box_data.area.has_changes = true;  // Mark area as having changes
×
1023
  // Update row changes
1024
  auto& changes_in_row = box_data.area.changes_in_row;
×
1025
  changes_in_row.ymin = std::min(changes_in_row.ymin, uInt(box_data.y1));
×
1026
  changes_in_row.ymax = std::max(changes_in_row.ymax, uInt(box_data.y2));
×
1027
}
1028

1029
//----------------------------------------------------------------------
1030
void updateStatusbar (const FWidget* w, bool need_focus)
×
1031
{
1032
  if ( w->hasFocus() != need_focus )
×
1033
    return;
×
1034

1035
  if ( auto sbar = w->getStatusBar() )
×
1036
  {
1037
    const auto& msg = w->getStatusbarMessage();
×
1038
    const auto& curMsg = sbar->getMessage();
×
1039

1040
    if ( curMsg != msg )
×
1041
    {
1042
      sbar->setMessage(msg);
×
1043
      sbar->drawMessage();
×
1044
    }
1045
  }
×
1046
}
1047

1048
//----------------------------------------------------------------------
1049
void drawStatusBarMessage()
×
1050
{
1051
  if ( FWidget::getStatusBar() )
×
1052
    FWidget::getStatusBar()->drawMessage();
×
1053
}
×
1054

1055
}  // namespace finalcut
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc