SimpleWindow
载入中...
搜索中...
未找到
Layer.h
1#pragma once
2
3#include "Dip.h"
4#include "LayoutHost.h"
5#include "ScrollEnums.h"
6#include "UIElement.h"
7#include <cmath>
8
9namespace sw
10{
11 template <typename TBase, typename = void>
12 class Layer; // 向前声明
13
17 template <typename TBase>
18 class Layer<TBase,
19 typename std::enable_if<std::is_base_of<UIElement, TBase>::value>::type>
20 : public TBase
21 {
22 private:
26 static constexpr int _LayerScrollBarLineInterval = 20;
27
28 private:
32 bool _autoSize = true;
33
37 LayoutHost *_customLayout = nullptr;
38
42 bool _horizontalScrollDisabled = true;
43
47 bool _verticalScrollDisabled = true;
48
52 bool _mouseWheelScrollEnabled = true;
53
54 public:
60 .Getter([](Layer *self) -> LayoutHost * {
61 return self->_customLayout;
62 })
63 .Setter([](Layer *self, LayoutHost *value) {
64 if (self->_customLayout != value) {
65 if (value != nullptr)
66 value->Associate(self);
67 self->_customLayout = value;
68 self->RaisePropertyChanged(&Layer::Layout);
69 self->InvalidateMeasure();
70 }
71 })};
72
78 .Getter([](Layer *self) -> bool {
79 return self->_autoSize;
80 })
81 .Setter([](Layer *self, bool value) {
82 if (self->_autoSize != value) {
83 self->_autoSize = value;
84 self->RaisePropertyChanged(&Layer::AutoSize);
85 self->InvalidateMeasure();
86 }
87 })};
88
92 const Property<bool> HorizontalScrollBar{
94 .Getter([](Layer *self) -> bool {
95 return self->GetStyle(WS_HSCROLL);
96 })
97 .Setter([](Layer *self, bool value) {
98 if (self->HorizontalScrollBar == value) {
99 return;
100 }
101 if (value) {
102 ShowScrollBar(self->Handle, SB_HORZ, value);
103 self->RaisePropertyChanged(&Layer::HorizontalScrollBar);
104 self->HorizontalScrollPos = self->HorizontalScrollPos;
105 } else {
106 self->HorizontalScrollPos = 0;
107 ShowScrollBar(self->Handle, SB_HORZ, value);
108 self->RaisePropertyChanged(&Layer::HorizontalScrollBar);
109 }
110 })};
111
115 const Property<bool> VerticalScrollBar{
117 .Getter([](Layer *self) -> bool {
118 return self->GetStyle(WS_VSCROLL);
119 })
120 .Setter([](Layer *self, bool value) {
121 if (self->VerticalScrollBar == value) {
122 return;
123 }
124 if (value) {
125 ShowScrollBar(self->Handle, SB_VERT, value);
126 self->RaisePropertyChanged(&Layer::VerticalScrollBar);
127 self->VerticalScrollPos = self->VerticalScrollPos;
128 } else {
129 self->VerticalScrollPos = 0;
130 ShowScrollBar(self->Handle, SB_VERT, value);
131 self->RaisePropertyChanged(&Layer::VerticalScrollBar);
132 }
133 })};
134
138 const Property<double> HorizontalScrollPos{
140 .Getter([](Layer *self) -> double {
142 info.cbSize = sizeof(info);
143 info.fMask = SIF_POS;
144 GetScrollInfo(self->Handle, SB_HORZ, &info);
145 return Dip::PxToDipX(info.nPos);
146 })
147 .Setter([](Layer *self, double value) {
148 self->_SetHorizontalScrollPos(value);
149 })};
150
154 const Property<double> VerticalScrollPos{
156 .Getter([](Layer *self) -> double {
158 info.cbSize = sizeof(info);
159 info.fMask = SIF_POS;
160 GetScrollInfo(self->Handle, SB_VERT, &info);
161 return Dip::PxToDipY(info.nPos);
162 })
163 .Setter(
164 [](Layer *self, double value) {
165 self->_SetVerticalScrollPos(value);
166 })};
167
171 const ReadOnlyProperty<double> HorizontalScrollLimit{
173 .Getter([](Layer *self) -> double {
174 if (self->_horizontalScrollDisabled) {
175 return 0;
176 }
178 info.cbSize = sizeof(info);
179 info.fMask = SIF_RANGE | SIF_PAGE;
180 GetScrollInfo(self->Handle, SB_HORZ, &info);
181 return Dip::PxToDipX(info.nMax - info.nPage + 1);
182 })};
183
187 const ReadOnlyProperty<double> VerticalScrollLimit{
189 .Getter([](Layer *self) -> double {
190 if (self->_verticalScrollDisabled) {
191 return 0;
192 }
194 info.cbSize = sizeof(info);
195 info.fMask = SIF_RANGE | SIF_PAGE;
196 GetScrollInfo(self->Handle, SB_VERT, &info);
197 return Dip::PxToDipY(info.nMax - info.nPage + 1);
198 })};
199
203 const Property<bool> MouseWheelScrollEnabled{
205 .Getter([](Layer *self) -> bool {
206 return self->_mouseWheelScrollEnabled;
207 })
208 .Setter([](Layer *self, bool value) {
209 if (self->_mouseWheelScrollEnabled != value) {
210 self->_mouseWheelScrollEnabled = value;
211 self->RaisePropertyChanged(&Layer::MouseWheelScrollEnabled);
212 }
213 })};
214
215 protected:
220 {
221 LayoutHost *layout = _GetLayout();
222
223 if (layout == nullptr) {
224 _MeasureAndArrangeWithoutLayout();
225 } else {
226 _MeasureAndArrangeWithoutResize(*layout, this->ClientRect->GetSize());
227 }
228
229 UpdateScrollRange();
230 }
231
236 {
237 return nullptr;
238 }
239
246 virtual void OnScroll(ScrollOrientation scrollbar, ScrollEvent event, double pos)
247 {
248 ScrollingEventArgs args(scrollbar, event, pos);
249 this->RaiseRoutedEvent(args);
250
251 if (args.cancel) {
252 return;
253 }
254
255 if (scrollbar == ScrollOrientation::Horizontal) {
256 // 水平滚动条
257 switch (event) {
259 HorizontalScrollPos = pos;
260 break;
261 }
262 case ScrollEvent::Left: {
263 ScrollToLeft();
264 break;
265 }
266 case ScrollEvent::Right: {
267 ScrollToRight();
268 break;
269 }
271 ScrollHorizontal(-GetHorizontalScrollPageSize());
272 break;
273 }
275 ScrollHorizontal(GetHorizontalScrollPageSize());
276 break;
277 }
279 ScrollHorizontal(-_LayerScrollBarLineInterval);
280 break;
281 }
283 ScrollHorizontal(_LayerScrollBarLineInterval);
284 break;
285 }
286 default: {
287 break;
288 }
289 }
290 } else {
291 // 垂直滚动条
292 switch (event) {
294 VerticalScrollPos = pos;
295 break;
296 }
297 case ScrollEvent::Bottom: {
298 ScrollToBottom();
299 break;
300 }
301 case ScrollEvent::Top: {
302 ScrollToTop();
303 break;
304 }
305 case ScrollEvent::PageUp: {
306 ScrollVertical(-GetVerticalScrollPageSize());
307 break;
308 }
310 ScrollVertical(GetVerticalScrollPageSize());
311 break;
312 }
313 case ScrollEvent::LineUp: {
314 ScrollVertical(-_LayerScrollBarLineInterval);
315 break;
316 }
318 ScrollVertical(_LayerScrollBarLineInterval);
319 break;
320 }
321 default: {
322 break;
323 }
324 }
325 }
326 }
327
334 virtual bool OnVerticalScroll(int event, int pos) override
335 {
337 (event == SB_THUMBTRACK || event == SB_THUMBPOSITION) ? Dip::PxToDipY(pos) : (0.0));
338 return true;
339 }
340
347 virtual bool OnHorizontalScroll(int event, int pos) override
348 {
350 (event == SB_THUMBTRACK || event == SB_THUMBPOSITION) ? Dip::PxToDipX(pos) : (0.0));
351 return true;
352 }
353
358 virtual void Arrange(const sw::Rect &finalPosition) override
359 {
360 TBase::Arrange(finalPosition);
361 UpdateScrollRange();
362 }
363
369 virtual Size MeasureOverride(const Size &availableSize) override
370 {
371 LayoutHost *layout = _GetLayout();
372
373 // 未设置布局时无法使用自动尺寸
374 // 若未使用自动尺寸,则按照普通元素measure
375 if (layout == nullptr || !_autoSize) {
376 return TBase::MeasureOverride(availableSize);
377 }
378
379 return layout->MeasureOverride(availableSize);
380 }
381
386 virtual void ArrangeOverride(const Size &finalSize) override
387 {
388 LayoutHost *layout = _GetLayout();
389
390 if (layout == nullptr) {
391 // 未设置布局方式,此时需要对子元素进行Measure和Arrange
392 _MeasureAndArrangeWithoutLayout();
393 } else if (!_autoSize) {
394 // 已设置布局方式,但是AutoSize被取消,此时子元素也未Measure
395 _MeasureAndArrangeWithoutResize(*layout, finalSize);
396 } else {
397 // 已设置布局方式且AutoSize为true,此时子元素已Measure,调用Arrange即可
398 layout->ArrangeOverride(finalSize);
399 }
400 }
401
407 virtual bool RequestBringIntoView(const sw::Rect &screenRect) override
408 {
409 bool handled = false;
410
411 sw::Point pos = this->PointFromScreen(screenRect.GetPos());
412 sw::Rect rect = {pos.x, pos.y, screenRect.width, screenRect.height};
413
414 sw::Rect clientRect = this->ClientRect;
415
416 if (VerticalScrollBar) //
417 {
418 double curPos = VerticalScrollPos;
419
420 if (rect.top < curPos) {
421 VerticalScrollPos = rect.top;
422 } else if (rect.top + rect.height > curPos + clientRect.height) {
423 if (rect.height >= clientRect.height) {
424 VerticalScrollPos = rect.top;
425 } else {
426 VerticalScrollPos = rect.top + rect.height - clientRect.height;
427 }
428 }
429 handled = true;
430 }
431
432 if (HorizontalScrollBar) //
433 {
434 double curPos = HorizontalScrollPos;
435
436 if (rect.left < curPos) {
437 HorizontalScrollPos = rect.left;
438 } else if (rect.left + rect.width > curPos + clientRect.width) {
439 if (rect.width >= clientRect.width) {
440 HorizontalScrollPos = rect.left;
441 } else {
442 HorizontalScrollPos = rect.left + rect.width - clientRect.width;
443 }
444 }
445 handled = true;
446 }
447
448 return handled;
449 }
450
457 {
458 TBase::OnRoutedEvent(eventArgs, handler);
459
460 if (!eventArgs.handled &&
461 eventArgs.eventType == UIElement_MouseWheel && _mouseWheelScrollEnabled) //
462 {
463 auto &wheelArgs = static_cast<MouseWheelEventArgs &>(eventArgs);
464 bool shiftDown = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
465 double offset = -std::copysign(_LayerScrollBarLineInterval, wheelArgs.wheelDelta);
466 if (shiftDown) {
467 if (HorizontalScrollBar) {
468 ScrollHorizontal(offset);
469 eventArgs.handled = true;
470 }
471 } else {
472 if (VerticalScrollBar) {
473 ScrollVertical(offset);
474 eventArgs.handled = true;
475 }
476 }
477 }
478 }
479
480 public:
487 {
488 INT nMin = 0, nMax = 0;
489 GetScrollRange(this->Handle, SB_HORZ, &nMin, &nMax);
490
493 }
494
500 void GetVerticalScrollRange(double &refMin, double &refMax)
501 {
502 INT nMin = 0, nMax = 0;
503 GetScrollRange(this->Handle, SB_VERT, &nMin, &nMax);
504
507 }
508
514 void SetHorizontalScrollRange(double min, double max)
515 {
517 info.cbSize = sizeof(info);
518 info.fMask = SIF_RANGE | SIF_PAGE;
519 info.nMin = Dip::DipToPxX(min);
520 info.nMax = Dip::DipToPxX(max);
521 info.nPage = Dip::DipToPxX(this->ClientWidth);
522
523 SetScrollInfo(this->Handle, SB_HORZ, &info, true);
524 }
525
531 void SetVerticalScrollRange(double min, double max)
532 {
534 info.cbSize = sizeof(info);
535 info.fMask = SIF_RANGE | SIF_PAGE;
536 info.nMin = Dip::DipToPxY(min);
537 info.nMax = Dip::DipToPxY(max);
538 info.nPage = Dip::DipToPxY(this->ClientHeight);
539
540 SetScrollInfo(this->Handle, SB_VERT, &info, true);
541 }
542
547 {
549 info.cbSize = sizeof(info);
550 info.fMask = SIF_PAGE;
551 GetScrollInfo(this->Handle, SB_HORZ, &info);
552 return Dip::PxToDipX(info.nPage);
553 }
554
559 {
561 info.cbSize = sizeof(info);
562 info.fMask = SIF_PAGE;
563 GetScrollInfo(this->Handle, SB_VERT, &info);
564 return Dip::PxToDipY(info.nPage);
565 }
566
571 {
573 info.cbSize = sizeof(info);
574 info.fMask = SIF_PAGE;
575 info.nPage = Dip::DipToPxX(pageSize);
576 SetScrollInfo(this->Handle, SB_HORZ, &info, true);
577 }
578
583 {
585 info.cbSize = sizeof(info);
586 info.fMask = SIF_PAGE;
587 info.nPage = Dip::DipToPxY(pageSize);
588 SetScrollInfo(this->Handle, SB_VERT, &info, true);
589 }
590
595 {
596 if (_GetLayout() == nullptr) {
597 // 当未设置布局方式时滚动条和控件位置需要手动设置
598 // 将以下俩字段设为false确保xxxScrollLimit属性在未设置布局方式时仍可用
599 _horizontalScrollDisabled = false;
600 _verticalScrollDisabled = false;
601 return;
602 }
603
604 if (HorizontalScrollBar) //
605 {
606 double childRightmost = this->GetChildRightmost(true);
607
608 if (int(childRightmost - this->ClientWidth) > 0) {
609 _horizontalScrollDisabled = false;
611 SetHorizontalScrollRange(0, childRightmost);
612
613 // 当尺寸改变时确保子元素位置与滚动条同步
614 double pos = HorizontalScrollPos;
615 if (-this->GetInternalArrangeOffsetX() > pos) {
616 HorizontalScrollPos = pos;
617 }
618 } else {
619 HorizontalScrollPos = 0;
621 _horizontalScrollDisabled = true;
622 }
623 }
624
625 if (VerticalScrollBar) //
626 {
627 double childBottommost = this->GetChildBottommost(true);
628
629 if (int(childBottommost - this->ClientHeight) > 0) {
630 _verticalScrollDisabled = false;
632 SetVerticalScrollRange(0, childBottommost);
633
634 // 当尺寸改变时确保子元素位置与滚动条同步
635 double pos = VerticalScrollPos;
636 if (-this->GetInternalArrangeOffsetY() > pos) {
637 VerticalScrollPos = pos;
638 }
639 } else {
640 VerticalScrollPos = 0;
642 _verticalScrollDisabled = true;
643 }
644 }
645 }
646
651 {
652 VerticalScrollPos = 0;
653 }
654
659 {
660 VerticalScrollPos = VerticalScrollLimit;
661 }
662
667 {
668 HorizontalScrollPos = 0;
669 }
670
675 {
676 HorizontalScrollPos = HorizontalScrollLimit;
677 }
678
684 {
685 HorizontalScrollPos += offset;
686 }
687
693 {
694 VerticalScrollPos += offset;
695 }
696
697 private:
701 LayoutHost *_GetLayout()
702 {
703 auto layout = (_customLayout != nullptr) ? _customLayout : GetDefaultLayout();
704 return (layout != nullptr && layout->IsAssociated(this)) ? layout : nullptr;
705 }
706
711 void _SetHorizontalScrollPos(double value)
712 {
713 if (HorizontalScrollPos != value) {
714 SCROLLINFO info{};
715 info.cbSize = sizeof(info);
716 info.fMask = SIF_POS;
717 info.nPos = Dip::DipToPxX(value);
718 SetScrollInfo(this->Handle, SB_HORZ, &info, true);
719 this->RaisePropertyChanged(&Layer::HorizontalScrollPos);
720 }
721
722 LayoutHost *layout = this->_GetLayout();
723
724 if (layout != nullptr && !this->_horizontalScrollDisabled && this->HorizontalScrollBar) {
725 this->GetInternalArrangeOffsetX() = -this->HorizontalScrollPos;
726 this->_MeasureAndArrangeWithoutResize(*layout, this->ClientRect->GetSize());
727 }
728 }
729
734 void _SetVerticalScrollPos(double value)
735 {
736 if (VerticalScrollPos != value) {
737 SCROLLINFO info{};
738 info.cbSize = sizeof(info);
739 info.fMask = SIF_POS;
740 info.nPos = Dip::DipToPxY(value);
741 SetScrollInfo(this->Handle, SB_VERT, &info, true);
742 this->RaisePropertyChanged(&Layer::VerticalScrollPos);
743 }
744
745 LayoutHost *layout = this->_GetLayout();
746
747 if (layout != nullptr && !this->_verticalScrollDisabled && this->VerticalScrollBar) {
748 this->GetInternalArrangeOffsetY() = -this->VerticalScrollPos;
749 this->_MeasureAndArrangeWithoutResize(*layout, this->ClientRect->GetSize());
750 }
751 }
752
756 void _MeasureAndArrangeWithoutLayout()
757 {
758 this->GetInternalArrangeOffsetX() = 0;
759 this->GetInternalArrangeOffsetY() = 0;
760
761 int childCount = this->GetChildLayoutCount();
762
763 for (int i = 0; i < childCount; ++i) {
764 // measure
765 UIElement &item = this->GetChildLayoutAt(i);
766 item.Measure(Size{INFINITY, INFINITY});
767 // arrange
768 Size desireSize = item.GetDesireSize();
769 sw::Rect itemRect = item.Rect;
770 Thickness itemMargin = item.Margin;
771 item.Arrange(sw::Rect{itemRect.left - itemMargin.left, itemRect.top - itemMargin.top, desireSize.width, desireSize.height});
772 }
773 }
774
778 void _MeasureAndArrangeWithoutResize(LayoutHost &layout, const Size &clientSize)
779 {
780 if (layout.IsAssociated(this)) {
781 layout.MeasureOverride(clientSize);
782 layout.ArrangeOverride(clientSize);
783 }
784 }
785 };
786
787 /*================================================================================*/
788
789 extern template class Layer<UIElement>;
790}
static int DipToPxY(double dip) noexcept
dip转像素(垂直方向)
static int DipToPxX(double dip) noexcept
dip转像素(水平方向)
static double PxToDipX(int px) noexcept
像素转dip(水平方向)
static double PxToDipY(int px) noexcept
像素转dip(垂直方向)
值转换器接口
Definition IValueConverter.h:14
virtual Size MeasureOverride(const Size &availableSize) override
测量元素所需尺寸,无需考虑边框和边距
Definition Layer.h:369
virtual void Arrange(const sw::Rect &finalPosition) override
安排元素位置
Definition Layer.h:358
virtual LayoutHost * GetDefaultLayout()
获取默认布局对象
Definition Layer.h:235
void SetVerticalScrollRange(double min, double max)
设置纵向滚动条的范围
Definition Layer.h:531
void ScrollToRight()
将水平滚动条移动到最右
Definition Layer.h:674
void UpdateScrollRange()
根据子元素更新滚动条范围,未设定布局方式时该函数无效
Definition Layer.h:594
virtual void ArrangeOverride(const Size &finalSize) override
安排子元素的位置,可重写该函数以实现自定义布局
Definition Layer.h:386
void SetHorizontalScrollPageSize(double pageSize)
设置水平滚动条滚动页面大小
Definition Layer.h:570
void ScrollToLeft()
将水平滚动条移动到最左
Definition Layer.h:666
void SetHorizontalScrollRange(double min, double max)
设置横向滚动条的范围
Definition Layer.h:514
void ScrollToBottom()
将垂直滚动条移动到底部
Definition Layer.h:658
void GetVerticalScrollRange(double &refMin, double &refMax)
获取纵向滚动条的范围
Definition Layer.h:500
virtual bool OnVerticalScroll(int event, int pos) override
接收到WM_VSCROLL时调用目标控件的该函数
Definition Layer.h:334
void SetVerticalScrollPageSize(double pageSize)
设置垂直滚动条滚动页面大小
Definition Layer.h:582
virtual void OnRoutedEvent(RoutedEventArgs &eventArgs, const RoutedEventHandler &handler) override
路由事件经过当前元素时调用该函数
Definition Layer.h:456
virtual void OnScroll(ScrollOrientation scrollbar, ScrollEvent event, double pos)
触发滚动条相关事件时调用该函数
Definition Layer.h:246
virtual bool RequestBringIntoView(const sw::Rect &screenRect) override
尝试将指定的矩形区域移动到可视区域内
Definition Layer.h:407
void ScrollToTop()
将垂直滚动条移动到顶部
Definition Layer.h:650
void GetHorizontalScrollRange(double &refMin, double &refMax)
获取横向滚动条的范围
Definition Layer.h:486
double GetVerticalScrollPageSize()
获取垂直滚动条滚动页面大小
Definition Layer.h:558
double GetHorizontalScrollPageSize()
获取水平滚动条滚动页面大小
Definition Layer.h:546
virtual bool OnHorizontalScroll(int event, int pos) override
接收到WM_HSCROLL时调用目标控件的该函数
Definition Layer.h:347
Definition Layer.h:12
用于托管元素的布局方式的对象类型,是所有布局方式类型的基类
Definition LayoutHost.h:11
鼠标滚轮滚动事件参数类型
Definition RoutedEventArgs.h:190
static auto Init() -> StaticPropertyInitializer< T >
获取静态属性初始化器
Definition Property.h:1554
窗口/面板滚动条滚动事件参数类型
Definition RoutedEventArgs.h:304
SimpleWindow框架的顶级命名空间,所有公共类型、控件、枚举和工具函数均定义于此。
Definition Alignment.h:4
ScrollOrientation
滚动条方向
Definition ScrollEnums.h:10
@ Vertical
垂直滚动条
@ Horizontal
水平滚动条
@ AutoSize
调整BmpBox大小,使其等于所包含位图的大小
ScrollEvent
滚动条事件
Definition ScrollEnums.h:18
@ Bottom
Scrolls to the lower right.
@ ThubmTrack
The user is dragging the scroll box. This message is sent repeatedly until the user releases the mous...
@ LineLeft
Scrolls left by one unit.
@ LineRight
Scrolls right by one unit.
@ LineDown
Scrolls one line down.
@ Right
Scrolls to the lower right.
@ Left
Scrolls to the upper left.
@ PageUp
Scrolls one page up.
@ LineUp
Scrolls one line up.
@ Top
Scrolls to the upper left.
@ PageRight
Scrolls right by the width of the window.
@ PageLeft
Scrolls left by the width of the window.
@ PageDown
Scrolls one page down.
@ UIElement_MouseWheel
鼠标滚轮滚动,参数类型为sw::MouseWheelEventArgs
Definition RoutedEvent.h:53
表示相对于左上角的点坐标
Definition Point.h:15
double y
纵坐标
Definition Point.h:24
double x
横坐标
Definition Point.h:19
表示一个矩形区域
Definition Rect.h:17
Rect()=default
默认构造函数
double top
顶边
Definition Rect.h:26
double left
左边
Definition Rect.h:21
路由事件的参数
Definition RoutedEvent.h:157
尺寸
Definition Size.h:15