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

gansm / finalcut / #760

06 Feb 2026 01:45AM UTC coverage: 69.064% (-0.02%) from 69.083%
#760

push

travis-ci

gansm
Reduce pointer arithmetic and use safe iterators more often

206 of 432 new or added lines in 27 files covered. (47.69%)

25 existing lines in 6 files now uncovered.

37622 of 54474 relevant lines covered (69.06%)

243.8 hits per line

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

0.0
/final/widget/fscrollview.cpp
1
/***********************************************************************
2
* fscrollview.cpp - Widget FScrollView (a scrolling area with          *
3
*                   on-demand scroll bars)                             *
4
*                                                                      *
5
* This file is part of the FINAL CUT widget toolkit                    *
6
*                                                                      *
7
* Copyright 2017-2026 Markus Gans                                      *
8
*                                                                      *
9
* FINAL CUT is free software; you can redistribute it and/or modify    *
10
* it under the terms of the GNU Lesser General Public License as       *
11
* published by the Free Software Foundation; either version 3 of       *
12
* the License, or (at your option) any later version.                  *
13
*                                                                      *
14
* FINAL CUT is distributed in the hope that it will be useful, but     *
15
* WITHOUT ANY WARRANTY; without even the implied warranty of           *
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
17
* GNU Lesser General Public License for more details.                  *
18
*                                                                      *
19
* You should have received a copy of the GNU Lesser General Public     *
20
* License along with this program.  If not, see                        *
21
* <http://www.gnu.org/licenses/>.                                      *
22
***********************************************************************/
23

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

27
#include "final/fevent.h"
28
#include "final/fwidgetcolors.h"
29
#include "final/vterm/fcolorpair.h"
30
#include "final/widget/fscrollview.h"
31
#include "final/widget/fstatusbar.h"
32
#include "final/widget/fwindow.h"
33

34
namespace finalcut
35
{
36

37
//----------------------------------------------------------------------
38
// class FScrollView
39
//----------------------------------------------------------------------
40

41
// constructors and destructor
42
//----------------------------------------------------------------------
43
FScrollView::FScrollView (FWidget* parent)
×
44
  : FWidget{parent}
×
45
{
46
  init();
×
47
}
×
48

49
//----------------------------------------------------------------------
50
FScrollView::~FScrollView()  // destructor
×
51
{
52
  setChildPrintArea (viewport.get());
×
53
  FWidget::delPreprocessingHandler(this);
×
54
}
×
55

56

57
// public methods of FScrollView
58
//----------------------------------------------------------------------
59
void FScrollView::setScrollWidth (std::size_t width)
×
60
{
61
  width = std::max(width, getViewportWidth());
×
62

63
  if ( getScrollWidth() == width )
×
64
    return;
×
65

66
  if ( viewport )
×
67
  {
68
    scroll_geometry.setWidth (width);
×
69
    resizeArea (scroll_geometry, viewport.get());
×
70
    setColor();
×
71
    FScrollView::clearArea();
×
72
    addLocalPreprocessingHandler();
×
73
    setChildPrintArea (viewport.get());
×
74
  }
75

76
  hbar->setMaximum (int(width - getViewportWidth()));
×
77
  hbar->setPageSize (int(width), int(getViewportWidth()));
×
78
  hbar->calculateSliderValues();
×
79

80
  if ( isShown() )
×
81
    setHorizontalScrollBarVisibility();
×
82
}
83

84
//----------------------------------------------------------------------
85
void FScrollView::setScrollHeight (std::size_t height)
×
86
{
87
  height = std::max(height, getViewportHeight());
×
88

89
  if ( getScrollHeight() == height )
×
90
    return;
×
91

92
  if ( viewport )
×
93
  {
94
    scroll_geometry.setHeight (height);
×
95
    resizeArea (scroll_geometry, viewport.get());
×
96
    setColor();
×
97
    clearArea();
×
98
    addLocalPreprocessingHandler();
×
99
    setChildPrintArea (viewport.get());
×
100
  }
101

102
  vbar->setMaximum (int(height - getViewportHeight()));
×
103
  vbar->setPageSize (int(height), int(getViewportHeight()));
×
104
  vbar->calculateSliderValues();
×
105

106
  if ( isShown() )
×
107
    setVerticalScrollBarVisibility();
×
108
}
109

110
//----------------------------------------------------------------------
111
void FScrollView::setScrollSize (const FSize& size)
×
112
{
113
  std::size_t width = std::max(size.getWidth(), getViewportWidth());
×
114
  std::size_t height = std::max(size.getHeight(), getViewportHeight());
×
115

116
  if ( getScrollWidth() == width && getScrollHeight() == height )
×
117
    return;
×
118

119
  if ( viewport )
×
120
  {
121
    scroll_geometry.setSize (width, height);
×
122
    resizeArea (scroll_geometry, viewport.get());
×
123
    setColor();
×
124
    FScrollView::clearArea();
×
125
    addLocalPreprocessingHandler();
×
126
    setChildPrintArea (viewport.get());
×
127
  }
128

129
  const auto xoffset_end = int(getScrollWidth() - getViewportWidth());
×
130
  const auto yoffset_end = int(getScrollHeight() - getViewportHeight());
×
131
  setTopPadding (1 - getScrollY());
×
132
  setLeftPadding (1 - getScrollX());
×
133
  setBottomPadding (1 - (yoffset_end - getScrollY()));
×
134
  setRightPadding (1 - (xoffset_end - getScrollX()) + int(nf_offset));
×
135

136
  hbar->setMaximum (int(width - getViewportWidth()));
×
137
  hbar->setPageSize (int(width), int(getViewportWidth()));
×
138
  hbar->calculateSliderValues();
×
139

140
  vbar->setMaximum (int(height - getViewportHeight()));
×
141
  vbar->setPageSize (int(height), int(getViewportHeight()));
×
142
  vbar->calculateSliderValues();
×
143

144
  if ( isShown() )
×
145
  {
146
    setHorizontalScrollBarVisibility();
×
147
    setVerticalScrollBarVisibility();
×
148
  }
149
}
150

151
//----------------------------------------------------------------------
152
void FScrollView::setX (int x, bool adjust)
×
153
{
154
  FWidget::setX (x, adjust);
×
155

156
  if ( adjust )
×
157
    return;
×
158

159
  scroll_geometry.setX (getTermX() + getLeftPadding() - 1);
×
160

161
  if ( viewport )
×
162
  {
163
    viewport->position.x = scroll_geometry.getX();
×
164
    viewport->position.y = scroll_geometry.getY();
×
165
  }
166
}
167

168
//----------------------------------------------------------------------
169
void FScrollView::setY (int y, bool adjust)
×
170
{
171
  FWidget::setY (y, adjust);
×
172

173
  if ( adjust )
×
174
    return;
×
175

176
  scroll_geometry.setY (getTermY() + getTopPadding() - 1);
×
177

178
  if ( viewport )
×
179
  {
180
    viewport->position.x = scroll_geometry.getX();
×
181
    viewport->position.y = scroll_geometry.getY();
×
182
  }
183
}
184

185
//----------------------------------------------------------------------
186
void FScrollView::setPos (const FPoint& p, bool adjust)
×
187
{
188
  FWidget::setPos (p, adjust);
×
189
  scroll_geometry.setPos ( getTermX() + getLeftPadding() - 1
×
190
                         , getTermY() + getTopPadding() - 1 );
×
191

192
  if ( adjust || ! viewport )
×
193
    return;
×
194

195
  viewport->position.x = scroll_geometry.getX();
×
196
  viewport->position.y = scroll_geometry.getY();
×
197
}
198

199
//----------------------------------------------------------------------
200
void FScrollView::setWidth (std::size_t w, bool adjust)
×
201
{
202
  if ( w <= vertical_border_spacing + nf_offset )
×
203
    return;
×
204

205
  FWidget::setWidth (w, adjust);
×
206
  viewport_geometry.setWidth(w - vertical_border_spacing - nf_offset);
×
207
  calculateScrollbarPos();
×
208

209
  if ( getScrollWidth() < getViewportWidth() )
×
210
    setScrollWidth (getViewportWidth());
×
211

212
  if ( ! viewport )
×
213
    return;
×
214

215
  // Insufficient space scrolling
216
  viewport_geometry.x1_ref() = \
×
217
      std::min ( int(getScrollWidth() - getViewportWidth())
×
218
               , viewport_geometry.getX1() );
×
219
}
220

221
//----------------------------------------------------------------------
222
void FScrollView::setHeight (std::size_t h, bool adjust)
×
223
{
224
  if ( h <= horizontal_border_spacing )
×
225
    return;
×
226

227
  FWidget::setHeight (h, adjust);
×
228
  viewport_geometry.setHeight(h - horizontal_border_spacing);
×
229
  calculateScrollbarPos();
×
230

231
  if ( getScrollHeight() < getViewportHeight() )
×
232
    setScrollHeight (getViewportHeight());
×
233

234
  if ( ! viewport )
×
235
    return;
×
236

237
  // Insufficient space scrolling
238
  viewport_geometry.y1_ref() = \
×
239
      std::min ( int(getScrollHeight() - getViewportHeight())
×
240
               , viewport_geometry.getY1() );
×
241
}
242

243
//----------------------------------------------------------------------
244
void FScrollView::setSize (const FSize& size, bool adjust)
×
245
{
246
  // Set the scroll view size
247

248
   if ( size.getWidth() <= vertical_border_spacing + nf_offset
×
249
     || size.getHeight() <= horizontal_border_spacing )
×
250
     return;
×
251

252
  FWidget::setSize (size, adjust);
×
253
  changeSize (size, adjust);
×
254
}
255

256
//----------------------------------------------------------------------
257
void FScrollView::setGeometry ( const FPoint& pos, const FSize& size
×
258
                              , bool adjust )
259
{
260
  // Set the scroll view geometry
261

262
  FWidget::setGeometry (pos, size, adjust);
×
263
  scroll_geometry.setPos ( getTermX() + getLeftPadding() - 1
×
264
                         , getTermY() + getTopPadding() - 1 );
×
265
  changeSize (size, adjust);
×
266
}
×
267

268
//----------------------------------------------------------------------
269
auto FScrollView::setCursorPos (const FPoint& p) -> bool
×
270
{
271
  return FWidget::setCursorPos ({ p.getX() + getLeftPadding()
×
272
                                , p.getY() + getTopPadding() });
×
273
}
274

275
//----------------------------------------------------------------------
276
void FScrollView::setPrintPos (const FPoint& p)
×
277
{
278
  FWidget::setPrintPos (FPoint { p.getX() + getLeftPadding()
×
279
                               , p.getY() + getTopPadding() });
×
280
}
×
281

282
//----------------------------------------------------------------------
283
void FScrollView::setText (const FString& txt)
×
284
{
285
  text.setString(txt);
×
286

287
  if ( isEnabled() )
×
288
  {
289
    delAccelerator();
×
290
    setHotkeyAccelerator();
×
291
  }
292
}
×
293

294
//----------------------------------------------------------------------
295
void FScrollView::setViewportPrint (bool enable)
×
296
{
297
  use_own_print_area = ! enable;
×
298
}
×
299

300
//----------------------------------------------------------------------
301
void FScrollView::resetColors()
×
302
{
303
  const auto& wc_dialog = getColorTheme()->dialog;
×
304
  FWidget::setForegroundColor (wc_dialog.fg);
×
305
  FWidget::setBackgroundColor (wc_dialog.bg);
×
306
  FWidget::resetColors();
×
307
}
×
308

309
//----------------------------------------------------------------------
310
void FScrollView::setBorder (bool enable)
×
311
{
312
  setFlags().feature.no_border = ! enable;
×
313
}
×
314

315
//----------------------------------------------------------------------
316
void FScrollView::setHorizontalScrollBarMode (ScrollBarMode mode)
×
317
{
318
  h_mode = mode;
×
319

320
  if ( isShown() )
×
321
    setHorizontalScrollBarVisibility();
×
322
}
×
323

324
//----------------------------------------------------------------------
325
void FScrollView::setVerticalScrollBarMode (ScrollBarMode mode)
×
326
{
327
  v_mode = mode;
×
328

329
  if ( isShown() )
×
330
    setVerticalScrollBarVisibility();
×
331
}
×
332

333
//----------------------------------------------------------------------
334
void FScrollView::clearArea (wchar_t fillchar)
×
335
{
336
  if ( viewport )
×
337
    FScrollView::clearArea (viewport.get(), fillchar);
×
338
}
×
339

340
//----------------------------------------------------------------------
341
void FScrollView::scrollToX (int x)
×
342
{
343
  scrollTo (x, viewport_geometry.getY() + 1);
×
344
}
×
345

346
//----------------------------------------------------------------------
347
void FScrollView::scrollToY (int y)
×
348
{
349
  scrollTo (viewport_geometry.getX() + 1, y);
×
350
}
×
351

352
//----------------------------------------------------------------------
353
void FScrollView::scrollTo (int x, int y)
×
354
{
355
  const int xoffset_before = viewport_geometry.getX1();
×
356
  const int yoffset_before = viewport_geometry.getY1();
×
357
  const auto xoffset_end = int(getScrollWidth() - getViewportWidth());
×
358
  const auto yoffset_end = int(getScrollHeight() - getViewportHeight());
×
359
  const std::size_t save_width = viewport_geometry.getWidth();
×
360
  const std::size_t save_height = viewport_geometry.getHeight();
×
361
  x--;
×
362
  y--;
×
363

364
  if ( ! isChangePosition(x, y) )
×
365
    return;
×
366

367
  adjustOffsets(x, y, xoffset_end, yoffset_end);
×
368
  const bool change_x = isChangeXPosition(xoffset_before);
×
369
  const bool change_y = isChangeYPosition(yoffset_before);
×
370

371
  if ( ! isShown() || ! viewport || ! (change_x || change_y) )
×
372
    return;
×
373

374
  if ( change_x )
×
375
    changeX (save_width, xoffset_end);
×
376

377
  if ( change_y )
×
378
    changeY (save_height, yoffset_end);
×
379

380
  viewport->has_changes = true;
×
381
  copy2area();
×
382
}
383

384
//----------------------------------------------------------------------
385
void FScrollView::scrollBy (int dx, int dy)
×
386
{
387
  scrollTo (1 + getScrollX() + dx, 1 + getScrollY() + dy);
×
388
}
×
389

390
//----------------------------------------------------------------------
391
void FScrollView::draw()
×
392
{
393
  unsetViewportPrint();
×
394

395
  if ( FVTerm::getFOutput()->isMonochron() )
×
396
    setReverse(true);
×
397

398
  if ( const auto& p = getParentWidget() )
×
399
    setColor (p->getForegroundColor(), p->getBackgroundColor());
×
400
  else
401
    setColor();
×
402

403
  if ( hasBorder() )
×
404
    drawBorder();
×
405

406
  if ( FVTerm::getFOutput()->isMonochron() )
×
407
    setReverse(false);
×
408

409
  setViewportPrint();
×
410
  copy2area();
×
411

412
  if ( ! hbar->isShown() )
×
413
    setHorizontalScrollBarVisibility();
×
414

415
  if ( ! vbar->isShown() )
×
416
    setVerticalScrollBarVisibility();
×
417

418
  vbar->redraw();
×
419
  hbar->redraw();
×
420
  drawLabel();
×
421
}
×
422

423
//----------------------------------------------------------------------
424
void FScrollView::drawBorder()
×
425
{
426
  const FRect box(FPoint{1, 1}, getSize());
×
427
  finalcut::drawListBorder (this, box);
×
428
}
×
429

430
//----------------------------------------------------------------------
431
void FScrollView::drawLabel()
×
432
{
433
  if ( text.isEmpty() )
×
434
    return;
×
435

436
  FString label_text{};
×
437
  const FString txt{" " + text + " "};
×
438
  unsetViewportPrint();
×
439
  const auto hotkeypos = finalcut::getHotkeyPos(txt, label_text);
×
440

441
  if ( hasBorder() )
×
442
    FWidget::setPrintPos (FPoint{2, 1});
×
443
  else
444
    FWidget::setPrintPos (FPoint{0, 1});
×
445

446
  drawText (label_text, hotkeypos);
×
447
  setViewportPrint();
×
448
}
×
449

450
//----------------------------------------------------------------------
451
void FScrollView::onKeyPress (FKeyEvent* ev)
×
452
{
453
  const auto& iter = key_map.find(ev->key());
×
454

455
  if ( iter != key_map.end() )
×
456
  {
457
    iter->second();
×
458
    ev->accept();
×
459
  }
460
}
×
461

462
//----------------------------------------------------------------------
463
void FScrollView::onMouseDown (FMouseEvent* ev)
×
464
{
465
  if ( ev->getButton() != MouseButton::Left )
×
466
    return;
×
467

468
  const int mouse_x = ev->getX();
×
469
  const int mouse_y = ev->getY();
×
470

471
  if ( mouse_x == 1 || mouse_x == int(getWidth())
×
472
    || mouse_y == 1 || mouse_y == int(getHeight()) )
×
473
  {
474
    directFocus();
×
475
  }
476

477
  // Event handover to parent dialog
478
  passResizeCornerEventToDialog(this, *ev);
×
479
}
480

481
//----------------------------------------------------------------------
482
void FScrollView::onMouseUp (FMouseEvent* ev)
×
483
{
484
  // Event handover to parent dialog
485
  passResizeCornerEventToDialog(this, *ev);
×
486
}
×
487

488
//----------------------------------------------------------------------
489
void FScrollView::onMouseMove (FMouseEvent* ev)
×
490
{
491
  // Event handover to parent dialog
492
  passResizeCornerEventToDialog(this, *ev);
×
493
}
×
494

495
//----------------------------------------------------------------------
496
void FScrollView::onWheel (FWheelEvent* ev)
×
497
{
498
  static constexpr int distance = 4;
499

500
  if ( ev->getWheel() == MouseWheel::Up )
×
501
  {
502
    scrollBy (0, -distance);
×
503
  }
504
  else if ( ev->getWheel() == MouseWheel::Down )
×
505
  {
506
    scrollBy (0, distance);
×
507
  }
508
  else if ( ev->getWheel() == MouseWheel::Left )
×
509
  {
510
    scrollBy (-distance, 0);
×
511
  }
512
  else if ( ev->getWheel() == MouseWheel::Right )
×
513
  {
514
    scrollBy (distance, 0);
×
515
  }
516
}
×
517

518
//----------------------------------------------------------------------
519
void FScrollView::onAccel (FAccelEvent*)
×
520
{
521
  directFocus();
×
522
}
×
523

524
//----------------------------------------------------------------------
525
void FScrollView::onFocusIn (FFocusEvent* in_ev)
×
526
{
527
  // Sets the focus to a child widget if it exists
528

529
  if ( ! hasChildren() )
×
530
    FWidget::onFocusIn(in_ev);
×
531

532
  if ( in_ev->getFocusType() == FocusTypes::NextWidget )
×
533
  {
534
    focusFirstChild() ? in_ev->accept() : in_ev->ignore();
×
535
  }
536
  else if ( in_ev->getFocusType() == FocusTypes::PreviousWidget )
×
537
  {
538
    focusLastChild() ? in_ev->accept() : in_ev->ignore();
×
539
  }
540
}
×
541

542
//----------------------------------------------------------------------
543
void FScrollView::onChildFocusIn (FFocusEvent*)
×
544
{
545
  // Scrolls the viewport so that the focused widget is visible
546

547
  const auto& focus = FWidget::getFocusWidget();
×
548

549
  if ( ! focus )
×
550
    return;
×
551

552
  const auto& widget_geometry = focus->getGeometryWithShadow();
×
553
  FRect vp_geometry = viewport_geometry;
×
554
  vp_geometry.move(1, 1);
×
555

556
  if ( ! vp_geometry.contains(widget_geometry) )
×
557
  {
558
    const int vx = vp_geometry.getX();
×
559
    const int vy = vp_geometry.getY();
×
560
    const int wx = widget_geometry.getX();
×
561
    const int wy = widget_geometry.getY();
×
562
    const auto width = int(vp_geometry.getWidth());
×
563
    const auto height = int(vp_geometry.getHeight());
×
564
    const int x = ( wx > vx ) ? widget_geometry.getX2() - width + 1 : wx;
×
565
    const int y = ( wy > vy ) ? widget_geometry.getY2() - height + 1 : wy;
×
566
    scrollTo (x, y);
×
567
  }
568
}
569

570
//----------------------------------------------------------------------
571
void FScrollView::onChildFocusOut (FFocusEvent* out_ev)
×
572
{
573
  // Change the focus away from FScrollView to another widget
574

575
  const auto* focus = FWidget::getFocusWidget();
×
576

577
  if ( out_ev->getFocusType() == FocusTypes::NextWidget )
×
578
  {
579
    const auto* last_widget = getLastFocusableWidget(getChildren());
×
580

581
    if ( focus != last_widget )
×
582
      return;
×
583

584
    out_ev->accept();
×
585
    focusNextChild();
×
586
  }
587
  else if ( out_ev->getFocusType() == FocusTypes::PreviousWidget )
×
588
  {
589
    const auto* first_widget = getFirstFocusableWidget(getChildren());
×
590

591
    if ( focus != first_widget )
×
592
      return;
×
593

594
    out_ev->accept();
×
595
    focusPrevChild();
×
596
  }
597
}
598

599
//----------------------------------------------------------------------
600
void FScrollView::onFailAtChildFocus (FFocusEvent* fail_ev)
×
601
{
602
  // Change the focus away from FScrollView to another widget
603

604
  if ( fail_ev->getFocusType() == FocusTypes::NextWidget )
×
605
  {
606
    fail_ev->accept();
×
607
    focusNextChild();
×
608
  }
609
  else if ( fail_ev->getFocusType() == FocusTypes::PreviousWidget )
×
610
  {
611
    fail_ev->accept();
×
612
    focusPrevChild();
×
613
  }
614
}
×
615

616

617
// protected methods of FScrollView
618
//----------------------------------------------------------------------
619
auto FScrollView::getPrintArea() -> FVTerm::FTermArea*
×
620
{
621
  // returns print area or viewport
622

623
  if ( use_own_print_area || ! viewport )
×
624
  {
625
    setChildPrintArea (nullptr);
×
626
    auto area = FWidget::getPrintArea();
×
627
    setChildPrintArea (viewport.get());
×
628
    return area;
×
629
  }
630

631
  return viewport.get();
×
632
}
633

634
//----------------------------------------------------------------------
635
void FScrollView::setHotkeyAccelerator()
×
636
{
637
  setHotkeyViaString (this, text);
×
638
}
×
639

640
//----------------------------------------------------------------------
641
void FScrollView::initLayout()
×
642
{
643
  nf_offset = FVTerm::getFOutput()->isNewFont() ? 1 : 0;
×
644
  const auto xoffset_end = int(getScrollWidth() - getViewportWidth());
×
645
  const auto yoffset_end = int(getScrollHeight() - getViewportHeight());
×
646
  setTopPadding (1 - getScrollY());
×
647
  setLeftPadding (1 - getScrollX());
×
648
  setBottomPadding (1 - (yoffset_end - getScrollY()));
×
649
  setRightPadding (1 - (xoffset_end - getScrollX()) + nf_offset);
×
650
  calculateScrollbarPos();
×
651
}
×
652

653
//----------------------------------------------------------------------
654
void FScrollView::adjustSize()
×
655
{
656
  FWidget::adjustSize();
×
657
  const std::size_t width = getWidth();
×
658
  const std::size_t height = getHeight();
×
659
  const int xoffset = viewport_geometry.getX();
×
660
  const int yoffset = viewport_geometry.getY();
×
661
  scroll_geometry.setPos ( getTermX() + getLeftPadding() - 1
×
662
                         , getTermY() + getTopPadding() - 1 );
×
663

664
  if ( viewport )
×
665
  {
666
    viewport->position.x = scroll_geometry.getX();
×
667
    viewport->position.y = scroll_geometry.getY();
×
668
  }
669

670
  vbar->setMaximum (int(getScrollHeight() - getViewportHeight()));
×
671
  vbar->setPageSize (int(getScrollHeight()), int(getViewportHeight()));
×
672
  vbar->setX (int(width));
×
673
  vbar->setHeight (height > 3 ? height - 2 : 1, false);
×
674
  vbar->setValue (yoffset);
×
675
  vbar->resize();
×
676

677
  hbar->setMaximum (int(getScrollWidth() - getViewportWidth()));
×
678
  hbar->setPageSize (int(getScrollWidth()), int(getViewportWidth()));
×
679
  hbar->setY (int(height));
×
680
  hbar->setWidth (width > 3 ? width - 2 : 1, false);
×
681
  hbar->setValue (xoffset);
×
682
  hbar->resize();
×
683

684
  setVerticalScrollBarVisibility();
×
685
  setHorizontalScrollBarVisibility();
×
686
}
×
687

688
//----------------------------------------------------------------------
689
void FScrollView::copy2area()
×
690
{
691
  // copy viewport to area
692

693
  if ( ! hasPrintArea() )
×
694
    FWidget::getPrintArea();
×
695

696
  if ( ! (hasPrintArea() && viewport && viewport->has_changes) )
×
697
    return;
×
698

699
  auto* printarea = getCurrentPrintArea();
×
700
  const auto* area_owner = printarea->getOwner<FVTerm*>();
×
701
  const auto* area_widget = static_cast<const FWidget*>(area_owner);
×
702

703
  const bool ignore_padding = getFlags().feature.ignore_padding;
×
704
  const int xoffset = ignore_padding ? 0 : area_widget->getLeftPadding();
×
705
  const int yoffset = ignore_padding ? 0 : area_widget->getTopPadding();
×
706
  const int ax = xoffset + getX();
×
707
  const int ay = yoffset + getY();
×
708
  const int dx = viewport_geometry.getX();
×
709
  const int dy = viewport_geometry.getY();
×
710
  const int rsh = printarea->shadow.width;
×
711
  const int area_width = printarea->size.width;
×
712
  const int area_height = printarea->size.height;
×
713
  const auto viewport_width = int(getViewportWidth());
×
714
  const auto viewport_height = int(getViewportHeight());
×
715

716
  // Calculate effective viewport dimensions within the printarea
717
  const int x_end = std::min(viewport_width, std::max(0, area_width - ax));
×
718
  const int y_end = std::min(viewport_height, std::max(0, area_height - ay));
×
719

720
  if ( x_end <= 0 || y_end <= 0 )
×
721
    return;  // Early exit if nothing needs copying
×
722

723
  const auto line_start = uInt(ax);
×
724
  const auto line_end = uInt(ax + x_end - 1);
×
725
  const auto max_limit = uInt(area_width + rsh - 1);
×
726

727
  for (int y{0}; y < y_end; y++)  // line loop
×
728
  {
729
    // Direct access to viewport and area characters
NEW
730
    const auto vc = viewport->getFCharIterator(dx, dy + y);  // Viewport character
×
NEW
731
    auto ac = printarea->getFCharIterator(ax, ay + y);       // Area character
×
732

733
    // Copy a line of characters in one operation
NEW
734
    std::memcpy (&ac[0], &vc[0], sizeof(FChar) * unsigned(x_end));
×
735

736
    // Update line changes
737
    auto& line_changes = printarea->changes_in_line[unsigned(ay + y)];
×
738
    line_changes.xmin = std::min({line_changes.xmin, line_start, max_limit});
×
739
    line_changes.xmax = std::min(std::max(line_changes.xmax, line_end), max_limit);
×
740
  }
741

742
  printarea->changes_in_row = {0, uInt(y_end - 1)};
×
743
  setViewportCursor();
×
744
  viewport->has_changes = false;
×
745
  printarea->has_changes = true;
×
746
}
747

748

749
// private methods of FScrollView
750
//----------------------------------------------------------------------
751
inline auto FScrollView::getViewportCursorPos() -> FPoint
×
752
{
753
  auto window = FWindow::getWindowWidget(this);
×
754

755
  if ( window )
×
756
  {
757
    const int widget_offsetX = getTermX() - window->getTermX();
×
758
    const int widget_offsetY = getTermY() - window->getTermY();
×
759
    const int x = widget_offsetX + viewport->input_cursor.x
×
760
                - viewport_geometry.getX();
×
761
    const int y = widget_offsetY + viewport->input_cursor.y
×
762
                - viewport_geometry.getY();
×
763
    return { x, y };
×
764
  }
765

766
  return { -1, -1 };
×
767
}
768

769
//----------------------------------------------------------------------
770
void FScrollView::init()
×
771
{
772
  const auto& parent = getParentWidget();
×
773

774
  assert ( parent != nullptr );
×
775
  assert ( ! parent->isInstanceOf("FScrollView") );
×
776

777
  initScrollbar (vbar, Orientation::Vertical, &FScrollView::cb_vbarChange);
×
778
  initScrollbar (hbar, Orientation::Horizontal, &FScrollView::cb_hbarChange);
×
779
  mapKeyFunctions();
×
780
  FScrollView::resetColors();
×
781
  FScrollView::setGeometry (FPoint{1, 1}, FSize{4, 4});
×
782
  setMinimumSize (FSize{4, 4});
×
783
  std::size_t width = std::max(std::size_t(1), getViewportWidth());
×
784
  std::size_t height = std::max(std::size_t(1), getViewportHeight());
×
785
  createViewport({ FSize{width, height} });
×
786
  addLocalPreprocessingHandler();
×
787

788
  if ( viewport )
×
789
    setChildPrintArea (viewport.get());
×
790
}
×
791

792
//----------------------------------------------------------------------
793
inline void FScrollView::addLocalPreprocessingHandler()
×
794
{
795
  if ( hasPreprocessingHandler(this) )
×
796
    return;
×
797

798
  FWidget::addPreprocessingHandler (this, [this] () { copy2area(); } );
×
799
}
800

801
//----------------------------------------------------------------------
802
inline void FScrollView::createViewport (const FSize& size) noexcept
×
803
{
804
  // Initialization of the scrollable viewport
805

806
  scroll_geometry.setSize(size);
×
807
  viewport = createArea(scroll_geometry);
×
808
  setColor();
×
809
  FScrollView::clearArea();
×
810
}
×
811

812
//----------------------------------------------------------------------
813
void FScrollView::drawText ( const FString& label_text
×
814
                           , std::size_t hotkeypos )
815
{
816
  setLabelStyle();
×
817
  printLabel (label_text, hotkeypos);
×
818
  printEllipsis (label_text);
×
819
}
×
820

821
//----------------------------------------------------------------------
822
inline auto FScrollView::getDisplayedTextLength ( const FString& label_text
×
823
                                                , const std::size_t column_width ) const -> std::size_t
824
{
825
  if ( column_width <= getClientWidth() )
×
826
    return label_text.getLength();
×
827

828
  const std::size_t len = getClientWidth() - 3;
×
829
  const FString s = finalcut::getColumnSubString (label_text, 1, len);
×
830
  return s.getLength();
×
831
}
×
832

833
//----------------------------------------------------------------------
834
void FScrollView::setLabelStyle() const
×
835
{
836
  if ( FVTerm::getFOutput()->isMonochron() )
×
837
    setReverse(true);
×
838

839
  const auto& wc_label = getColorTheme()->label;
×
840

841
  if ( isEnabled() )
×
842
    setColor(wc_label.emphasis_fg, wc_label.bg);
×
843
  else
844
    setColor(wc_label.inactive_fg, wc_label.inactive_bg);
×
845
}
×
846

847
//----------------------------------------------------------------------
848
void FScrollView::printLabel ( const FString& label_text,
×
849
                               std::size_t hotkeypos )
850
{
851
  const auto& wc_label = getColorTheme()->label;
×
852
  const std::size_t column_width = getColumnWidth(label_text);
×
853
  std::size_t length = getDisplayedTextLength (label_text, column_width);
×
854
  const auto underline = ! getFlags().feature.no_underline;
×
855

856
  for (std::size_t z{0}; z < length; z++)
×
857
  {
858
    if ( z == hotkeypos && getFlags().feature.active )
×
859
    {
860
      setColor (wc_label.hotkey_fg, wc_label.hotkey_bg);
×
861

862
      if ( underline )
×
863
        setUnderline();
×
864

865
      print (label_text[z]);
×
866

867
      if ( underline )
×
868
        unsetUnderline();
×
869

870
      setColor (wc_label.emphasis_fg, wc_label.bg);
×
871
    }
872
    else
873
      print (label_text[z]);
×
874
  }
875
}
×
876

877
//----------------------------------------------------------------------
878
void FScrollView::printEllipsis (const FString& label_text)
×
879
{
880
  const auto& wc_label = getColorTheme()->label;
×
881
  const std::size_t column_width = getColumnWidth(label_text);
×
882
  const bool ellipsis{column_width > getClientWidth()};
×
883

884
  if ( ellipsis )  // Print ellipsis
×
885
    print() << FColorPair {wc_label.ellipsis_fg, wc_label.bg} << "..";
×
886

887
  if ( FVTerm::getFOutput()->isMonochron() )
×
888
    setReverse(true);
×
889
}
×
890

891
//----------------------------------------------------------------------
892
void FScrollView::directFocus()
×
893
{
894
  auto focused_widget = getFocusWidget();
×
895
  focusFirstChild();
×
896

897
  if ( focused_widget )
×
898
    focused_widget->redraw();
×
899

900
  focused_widget = getFocusWidget();
×
901

902
  if ( focused_widget )
×
903
    focused_widget->redraw();
×
904

905
  drawStatusBarMessage();
×
906
}
×
907

908
//----------------------------------------------------------------------
909
inline void FScrollView::mapKeyFunctions()
×
910
{
911
  auto scrollToEnd = [this] ()
×
912
  {
913
    auto yoffset_end = int(getScrollHeight() - getViewportHeight());
×
914
    scrollToY (1 + yoffset_end);
×
915
  };
×
916

917
  key_map =
×
918
  {
919
    { FKey::Up        , [this] { scrollBy (0, -1); } },
×
920
    { FKey::Down      , [this] { scrollBy (0, 1); } },
×
921
    { FKey::Left      , [this] { scrollBy (-1, 0); } },
×
922
    { FKey::Right     , [this] { scrollBy (1, 0); } },
×
923
    { FKey::Page_up   , [this] { scrollBy (0, -int(getViewportHeight())); } },
×
924
    { FKey::Page_down , [this] { scrollBy (0, int(getViewportHeight())); } },
×
925
    { FKey::Home      , [this] { scrollToY (1); } },
×
926
    { FKey::End       , scrollToEnd }
×
927
  };
×
928
}
×
929

930
//----------------------------------------------------------------------
931
void FScrollView::changeSize (const FSize& size, bool adjust)
×
932
{
933
  const std::size_t w = size.getWidth();
×
934
  const std::size_t h = size.getHeight();
×
935
  viewport_geometry.setSize ( w - vertical_border_spacing - nf_offset
×
936
                            , h - horizontal_border_spacing );
937
  calculateScrollbarPos();
×
938

939
  if ( getScrollWidth() < getViewportWidth()
×
940
    || getScrollHeight() < getViewportHeight() )
×
941
  {
942
    FScrollView::setScrollSize (getViewportSize());
×
943
  }
944
  else if ( ! adjust && viewport )
×
945
  {
946
    viewport->position.x = scroll_geometry.getX();
×
947
    viewport->position.y = scroll_geometry.getY();
×
948
  }
949

950
  if ( ! viewport )
×
951
    return;
×
952

953
  // Insufficient space scrolling
954
  viewport_geometry.x1_ref() = \
×
955
      std::min ( int(getScrollWidth() - getViewportWidth())
×
956
               , viewport_geometry.getX1() );
×
957
  viewport_geometry.y1_ref() = \
×
958
      std::min ( int(getScrollHeight() - getViewportHeight())
×
959
               , viewport_geometry.getY1() );
×
960
}
961

962
//----------------------------------------------------------------------
963
inline void FScrollView::adjustOffsets ( int x, int y
×
964
                                       , int xoffset_end
965
                                       , int yoffset_end )
966
{
967
  int& xoffset = viewport_geometry.x1_ref();
×
968
  int& yoffset = viewport_geometry.y1_ref();
×
969
  xoffset = std::max(x, 0);
×
970
  xoffset = std::min(xoffset, xoffset_end);
×
971
  yoffset = std::max(y, 0);
×
972
  yoffset = std::min(yoffset, yoffset_end);
×
973
}
×
974

975
//----------------------------------------------------------------------
976
inline auto FScrollView::isChangePosition (const int x, const int y) const -> bool
×
977
{
978
  return isChangeXPosition(x) || isChangeYPosition(y);
×
979
}
980

981
//----------------------------------------------------------------------
982
inline auto FScrollView::isChangeXPosition (const int x) const -> bool
×
983
{
984
  return viewport_geometry.getX1() != x;
×
985
}
986

987
//----------------------------------------------------------------------
988
inline auto FScrollView::isChangeYPosition (const int y) const -> bool
×
989
{
990
  return viewport_geometry.getY1() != y;
×
991
}
992

993
//----------------------------------------------------------------------
994
inline void FScrollView::changeX (const std::size_t width, const int xoffset_end)
×
995
{
996
  const int xoffset = viewport_geometry.getX1();
×
997
  viewport_geometry.setWidth(width);
×
998
  setLeftPadding (1 - xoffset);
×
999
  setRightPadding (1 - (xoffset_end - xoffset) + int(nf_offset));
×
1000

1001
  if ( ! update_scrollbar )
×
1002
    return;
×
1003

1004
  hbar->setValue (xoffset);
×
1005
  hbar->drawBar();
×
1006
}
1007

1008
//----------------------------------------------------------------------
1009
inline void FScrollView::changeY (const std::size_t height, const int yoffset_end)
×
1010
{
1011
  const int yoffset = viewport_geometry.getY1();
×
1012
  viewport_geometry.setHeight(height);
×
1013
  setTopPadding (1 - yoffset);
×
1014
  setBottomPadding (1 - (yoffset_end - yoffset));
×
1015

1016
  if ( ! update_scrollbar )
×
1017
    return;
×
1018

1019
  vbar->setValue (yoffset);
×
1020
  vbar->drawBar();
×
1021
}
1022

1023
//----------------------------------------------------------------------
1024
void FScrollView::calculateScrollbarPos() const
×
1025
{
1026
  const std::size_t width  = std::max(std::size_t(3), getWidth());
×
1027
  const std::size_t height = std::max(std::size_t(3), getHeight());
×
1028

1029
  if ( nf_offset )
×
1030
  {
1031
    vbar->setGeometry (FPoint{int(width), 2}, FSize{2, height - 2});
×
1032
    hbar->setGeometry (FPoint{1, int(height)}, FSize{width - 2, 1});
×
1033
  }
1034
  else
1035
  {
1036
    vbar->setGeometry (FPoint{int(width), 2}, FSize{1, height - 2});
×
1037
    hbar->setGeometry (FPoint{2, int(height)}, FSize{width - 2, 1});
×
1038
  }
1039

1040
  vbar->resize();
×
1041
  hbar->resize();
×
1042
}
×
1043

1044
//----------------------------------------------------------------------
1045
void FScrollView::setHorizontalScrollBarVisibility() const
×
1046
{
1047
  if ( h_mode == ScrollBarMode::Auto )
×
1048
  {
1049
    if ( getScrollWidth() > getViewportWidth() )
×
1050
      hbar->show();
×
1051
    else
1052
      hbar->hide();
×
1053
  }
1054
  else if ( h_mode == ScrollBarMode::Hidden )
×
1055
  {
1056
    hbar->hide();
×
1057
  }
1058
  else if ( h_mode == ScrollBarMode::Scroll )
×
1059
  {
1060
    hbar->show();
×
1061
  }
1062
}
×
1063

1064
//----------------------------------------------------------------------
1065
void FScrollView::setVerticalScrollBarVisibility() const
×
1066
{
1067
  if ( v_mode == ScrollBarMode::Auto )
×
1068
  {
1069
    if ( getScrollHeight() > getViewportHeight() )
×
1070
      vbar->show();
×
1071
    else
1072
      vbar->hide();
×
1073
  }
1074
  else if ( v_mode == ScrollBarMode::Hidden )
×
1075
  {
1076
    vbar->hide();
×
1077
  }
1078
  else if ( v_mode == ScrollBarMode::Scroll )
×
1079
  {
1080
    vbar->show();
×
1081
  }
1082
}
×
1083

1084
//----------------------------------------------------------------------
1085
void FScrollView::setViewportCursor()
×
1086
{
1087
  if ( ! isChild(getFocusWidget()) )
×
1088
    return;
×
1089

1090
  const FPoint cursor_pos { viewport->input_cursor.x - 1
×
1091
                          , viewport->input_cursor.y - 1 };
×
1092
  const FPoint window_cursor_pos{ getViewportCursorPos() };
×
1093
  auto printarea = getCurrentPrintArea();
×
1094
  printarea->setInputCursorPos ( window_cursor_pos.getX()
×
1095
                               , window_cursor_pos.getY() );
1096

1097
  if ( viewport->input_cursor_visible
×
1098
    && viewport_geometry.contains(cursor_pos) )
×
1099
    printarea->input_cursor_visible = true;
×
1100
  else
1101
    printarea->input_cursor_visible = false;
×
1102
}
1103

1104
//----------------------------------------------------------------------
1105
inline auto FScrollView::shouldUpdateScrollbar (FScrollbar::ScrollType scroll_type) const -> bool
×
1106
{
1107
  return scroll_type >= FScrollbar::ScrollType::StepBackward;
×
1108
}
1109

1110
//----------------------------------------------------------------------
1111
inline auto FScrollView::getVerticalScrollDistance (const FScrollbar::ScrollType scroll_type) const -> int
×
1112
{
1113
  if ( scroll_type == FScrollbar::ScrollType::PageBackward
×
1114
    || scroll_type == FScrollbar::ScrollType::PageForward )
×
1115
  {
1116
    return int(getViewportHeight());
×
1117
  }
1118

1119
  return 1;
×
1120
}
1121

1122
//----------------------------------------------------------------------
1123
inline auto FScrollView::getHorizontalScrollDistance (const FScrollbar::ScrollType scroll_type) const -> int
×
1124
{
1125
  if ( scroll_type == FScrollbar::ScrollType::PageBackward
×
1126
    || scroll_type == FScrollbar::ScrollType::PageForward )
×
1127
  {
1128
    return int(getViewportWidth());
×
1129
  }
1130

1131
  return 1;
×
1132
}
1133

1134
//----------------------------------------------------------------------
1135
void FScrollView::cb_vbarChange (const FWidget*)
×
1136
{
1137
  auto scroll_type = vbar->getScrollType();
×
1138
  update_scrollbar = shouldUpdateScrollbar(scroll_type);
×
1139
  static constexpr int wheel_distance = 4;
1140
  int distance = getVerticalScrollDistance(scroll_type);
×
1141

1142
  switch ( scroll_type )
×
1143
  {
1144
    case FScrollbar::ScrollType::PageBackward:
×
1145
    case FScrollbar::ScrollType::StepBackward:
1146
      scrollBy (0, -distance);
×
1147
      break;
×
1148

1149
    case FScrollbar::ScrollType::PageForward:
×
1150
    case FScrollbar::ScrollType::StepForward:
1151
      scrollBy (0, distance);
×
1152
      break;
×
1153

1154
    case FScrollbar::ScrollType::Jump:
×
1155
      scrollToY (1 + int(vbar->getValue()));
×
1156
      break;
×
1157

1158
    case FScrollbar::ScrollType::WheelUp:
×
1159
    case FScrollbar::ScrollType::WheelLeft:
1160
      scrollBy (0, -wheel_distance);
×
1161
      break;
×
1162

1163
    case FScrollbar::ScrollType::WheelDown:
×
1164
    case FScrollbar::ScrollType::WheelRight:
1165
      scrollBy (0, wheel_distance);
×
1166
      break;
×
1167

1168
    default:
×
1169
      throw std::invalid_argument{"Invalid scroll type"};
×
1170
  }
1171

1172
  update_scrollbar = true;
×
1173
}
×
1174

1175
//----------------------------------------------------------------------
1176
void FScrollView::cb_hbarChange (const FWidget*)
×
1177
{
1178
  auto scroll_type = hbar->getScrollType();
×
1179
  update_scrollbar = shouldUpdateScrollbar(scroll_type);
×
1180
  static constexpr int wheel_distance = 4;
1181
  int distance = getHorizontalScrollDistance(scroll_type);
×
1182

1183
  switch ( scroll_type )
×
1184
  {
1185
    case FScrollbar::ScrollType::PageBackward:
×
1186
    case FScrollbar::ScrollType::StepBackward:
1187
      scrollBy (-distance, 0);
×
1188
      break;
×
1189

1190
    case FScrollbar::ScrollType::PageForward:
×
1191
    case FScrollbar::ScrollType::StepForward:
1192
      scrollBy (distance, 0);
×
1193
      break;
×
1194

1195
    case FScrollbar::ScrollType::Jump:
×
1196
      scrollToX (1 + int(hbar->getValue()));
×
1197
      break;
×
1198

1199
    case FScrollbar::ScrollType::WheelUp:
×
1200
    case FScrollbar::ScrollType::WheelLeft:
1201
      scrollBy (-wheel_distance, 0);
×
1202
      break;
×
1203

1204
    case FScrollbar::ScrollType::WheelDown:
×
1205
    case FScrollbar::ScrollType::WheelRight:
1206
      scrollBy (wheel_distance, 0);
×
1207
      break;
×
1208

1209
    default:
×
1210
      throw std::invalid_argument{"Invalid scroll type"};
×
1211
  }
1212

1213
  update_scrollbar = true;
×
1214
}
×
1215

1216
// FVTerm friend function definition
1217
//----------------------------------------------------------------------
1218
void setPrintArea (FWidget& widget, FVTerm::FTermArea* area)
×
1219
{
1220
  widget.print_area = area;
×
1221
}
×
1222

1223
}  // 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