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 (value != nullptr)
65 value->Associate(self);
66 self->_customLayout = value;
67 self->InvalidateMeasure();
68 })};
69
75 .Getter([](Layer *self) -> bool {
76 return self->_autoSize;
77 })
78 .Setter([](Layer *self, bool value) {
79 if (self->_autoSize != value) {
80 self->_autoSize = value;
81 self->InvalidateMeasure();
82 }
83 })};
84
88 const Property<bool> HorizontalScrollBar{
90 .Getter([](Layer *self) -> bool {
91 return self->GetStyle(WS_HSCROLL);
92 })
93 .Setter([](Layer *self, bool value) {
94 if (self->HorizontalScrollBar == value) {
95 return;
96 }
97 if (value) {
98 ShowScrollBar(self->Handle, SB_HORZ, value);
99 self->HorizontalScrollPos = self->HorizontalScrollPos;
100 } else {
101 self->HorizontalScrollPos = 0;
102 ShowScrollBar(self->Handle, SB_HORZ, value);
103 }
104 })};
105
109 const Property<bool> VerticalScrollBar{
111 .Getter([](Layer *self) -> bool {
112 return self->GetStyle(WS_VSCROLL);
113 })
114 .Setter([](Layer *self, bool value) {
115 if (self->VerticalScrollBar == value) {
116 return;
117 }
118 if (value) {
119 ShowScrollBar(self->Handle, SB_VERT, value);
120 self->VerticalScrollPos = self->VerticalScrollPos;
121 } else {
122 self->VerticalScrollPos = 0;
123 ShowScrollBar(self->Handle, SB_VERT, value);
124 }
125 })};
126
130 const Property<double> HorizontalScrollPos{
132 .Getter([](Layer *self) -> double {
134 info.cbSize = sizeof(info);
135 info.fMask = SIF_POS;
136 GetScrollInfo(self->Handle, SB_HORZ, &info);
137 return Dip::PxToDipX(info.nPos);
138 })
139 .Setter([](Layer *self, double value) {
141 info.cbSize = sizeof(info);
142 info.fMask = SIF_POS;
143 info.nPos = Dip::DipToPxX(value);
144 SetScrollInfo(self->Handle, SB_HORZ, &info, true);
145
146 LayoutHost *layout = self->_GetLayout();
147
148 if (layout != nullptr && !self->_horizontalScrollDisabled && self->HorizontalScrollBar) {
149 self->GetInternalArrangeOffsetX() = -self->HorizontalScrollPos;
150 self->_MeasureAndArrangeWithoutResize(*layout, self->ClientRect->GetSize());
151 }
152 })};
153
157 const Property<double> VerticalScrollPos{
159 .Getter([](Layer *self) -> double {
161 info.cbSize = sizeof(info);
162 info.fMask = SIF_POS;
163 GetScrollInfo(self->Handle, SB_VERT, &info);
164 return Dip::PxToDipY(info.nPos);
165 })
166 .Setter(
167 [](Layer *self, double value) {
169 info.cbSize = sizeof(info);
170 info.fMask = SIF_POS;
171 info.nPos = Dip::DipToPxY(value);
172 SetScrollInfo(self->Handle, SB_VERT, &info, true);
173
174 LayoutHost *layout = self->_GetLayout();
175
176 if (layout != nullptr && !self->_verticalScrollDisabled && self->VerticalScrollBar) {
177 self->GetInternalArrangeOffsetY() = -self->VerticalScrollPos;
178 self->_MeasureAndArrangeWithoutResize(*layout, self->ClientRect->GetSize());
179 }
180 })};
181
185 const ReadOnlyProperty<double> HorizontalScrollLimit{
187 .Getter([](Layer *self) -> double {
188 if (self->_horizontalScrollDisabled) {
189 return 0;
190 }
192 info.cbSize = sizeof(info);
193 info.fMask = SIF_RANGE | SIF_PAGE;
194 GetScrollInfo(self->Handle, SB_HORZ, &info);
195 return Dip::PxToDipX(info.nMax - info.nPage + 1);
196 })};
197
201 const ReadOnlyProperty<double> VerticalScrollLimit{
203 .Getter([](Layer *self) -> double {
204 if (self->_verticalScrollDisabled) {
205 return 0;
206 }
208 info.cbSize = sizeof(info);
209 info.fMask = SIF_RANGE | SIF_PAGE;
210 GetScrollInfo(self->Handle, SB_VERT, &info);
211 return Dip::PxToDipY(info.nMax - info.nPage + 1);
212 })};
213
217 const Property<bool> MouseWheelScrollEnabled{
219 .Getter([](Layer *self) -> bool {
220 return self->_mouseWheelScrollEnabled;
221 })
222 .Setter([](Layer *self, bool value) {
223 self->_mouseWheelScrollEnabled = value;
224 })};
225
226 protected:
231 {
232 LayoutHost *layout = _GetLayout();
233
234 if (layout == nullptr) {
235 _MeasureAndArrangeWithoutLayout();
236 } else {
237 _MeasureAndArrangeWithoutResize(*layout, this->ClientRect->GetSize());
238 }
239
240 UpdateScrollRange();
241 // Redraw();
242 }
243
248 {
249 return nullptr;
250 }
251
258 virtual void OnScroll(ScrollOrientation scrollbar, ScrollEvent event, double pos)
259 {
260 ScrollingEventArgs args(scrollbar, event, pos);
261 this->RaiseRoutedEvent(args);
262
263 if (args.cancel) {
264 return;
265 }
266
267 if (scrollbar == ScrollOrientation::Horizontal) {
268 // 水平滚动条
269 switch (event) {
271 HorizontalScrollPos = pos;
272 break;
273 }
274 case ScrollEvent::Left: {
275 ScrollToLeft();
276 break;
277 }
278 case ScrollEvent::Right: {
279 ScrollToRight();
280 break;
281 }
283 ScrollHorizontal(-GetHorizontalScrollPageSize());
284 break;
285 }
287 ScrollHorizontal(GetHorizontalScrollPageSize());
288 break;
289 }
291 ScrollHorizontal(-_LayerScrollBarLineInterval);
292 break;
293 }
295 ScrollHorizontal(_LayerScrollBarLineInterval);
296 break;
297 }
298 default: {
299 break;
300 }
301 }
302 } else {
303 // 垂直滚动条
304 switch (event) {
306 VerticalScrollPos = pos;
307 break;
308 }
309 case ScrollEvent::Bottom: {
310 ScrollToBottom();
311 break;
312 }
313 case ScrollEvent::Top: {
314 ScrollToTop();
315 break;
316 }
317 case ScrollEvent::PageUp: {
318 ScrollVertical(-GetVerticalScrollPageSize());
319 break;
320 }
322 ScrollVertical(GetVerticalScrollPageSize());
323 break;
324 }
325 case ScrollEvent::LineUp: {
326 ScrollVertical(-_LayerScrollBarLineInterval);
327 break;
328 }
330 ScrollVertical(_LayerScrollBarLineInterval);
331 break;
332 }
333 default: {
334 break;
335 }
336 }
337 }
338 }
339
346 virtual bool OnVerticalScroll(int event, int pos) override
347 {
349 (event == SB_THUMBTRACK || event == SB_THUMBPOSITION) ? Dip::PxToDipY(pos) : (0.0));
350 return true;
351 }
352
359 virtual bool OnHorizontalScroll(int event, int pos) override
360 {
362 (event == SB_THUMBTRACK || event == SB_THUMBPOSITION) ? Dip::PxToDipX(pos) : (0.0));
363 return true;
364 }
365
370 virtual void Arrange(const sw::Rect &finalPosition) override
371 {
372 TBase::Arrange(finalPosition);
373 UpdateScrollRange();
374 }
375
381 virtual Size MeasureOverride(const Size &availableSize) override
382 {
383 LayoutHost *layout = _GetLayout();
384
385 // 未设置布局时无法使用自动尺寸
386 // 若未使用自动尺寸,则按照普通元素measure
387 if (layout == nullptr || !_autoSize) {
388 return TBase::MeasureOverride(availableSize);
389 }
390
391 return layout->MeasureOverride(availableSize);
392 }
393
398 virtual void ArrangeOverride(const Size &finalSize) override
399 {
400 LayoutHost *layout = _GetLayout();
401
402 if (layout == nullptr) {
403 // 未设置布局方式,此时需要对子元素进行Measure和Arrange
404 _MeasureAndArrangeWithoutLayout();
405 } else if (!_autoSize) {
406 // 已设置布局方式,但是AutoSize被取消,此时子元素也未Measure
407 _MeasureAndArrangeWithoutResize(*layout, finalSize);
408 } else {
409 // 已设置布局方式且AutoSize为true,此时子元素已Measure,调用Arrange即可
410 layout->ArrangeOverride(finalSize);
411 }
412 }
413
419 virtual bool RequestBringIntoView(const sw::Rect &screenRect) override
420 {
421 bool handled = false;
422
423 sw::Point pos = this->PointFromScreen(screenRect.GetPos());
424 sw::Rect rect = {pos.x, pos.y, screenRect.width, screenRect.height};
425
426 sw::Rect clientRect = this->ClientRect;
427
428 if (VerticalScrollBar) {
429 double curPos = VerticalScrollPos;
430 if (rect.top < curPos) {
431 VerticalScrollPos = rect.top;
432 } else if (rect.top + rect.height > curPos + clientRect.height) {
433 if (rect.height >= clientRect.height) {
434 VerticalScrollPos = rect.top;
435 } else {
436 VerticalScrollPos = rect.top + rect.height - clientRect.height;
437 }
438 }
439 handled = true;
440 }
441
442 if (HorizontalScrollBar) {
443 double curPos = HorizontalScrollPos;
444 if (rect.left < curPos) {
445 HorizontalScrollPos = rect.left;
446 } else if (rect.left + rect.width > curPos + clientRect.width) {
447 if (rect.width >= clientRect.width) {
448 HorizontalScrollPos = rect.left;
449 } else {
450 HorizontalScrollPos = rect.left + rect.width - clientRect.width;
451 }
452 }
453 handled = true;
454 }
455
456 return handled;
457 }
458
465 {
466 TBase::OnRoutedEvent(eventArgs, handler);
467
468 if (!eventArgs.handled &&
469 eventArgs.eventType == UIElement_MouseWheel && _mouseWheelScrollEnabled) //
470 {
471 auto &wheelArgs = static_cast<MouseWheelEventArgs &>(eventArgs);
472 bool shiftDown = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
473 double offset = -std::copysign(_LayerScrollBarLineInterval, wheelArgs.wheelDelta);
474 if (shiftDown) {
475 if (HorizontalScrollBar) {
476 ScrollHorizontal(offset);
477 eventArgs.handled = true;
478 }
479 } else {
480 if (VerticalScrollBar) {
481 ScrollVertical(offset);
482 eventArgs.handled = true;
483 }
484 }
485 }
486 }
487
488 public:
495 {
496 INT nMin = 0, nMax = 0;
497 GetScrollRange(this->Handle, SB_HORZ, &nMin, &nMax);
498
501 }
502
508 void GetVerticalScrollRange(double &refMin, double &refMax)
509 {
510 INT nMin = 0, nMax = 0;
511 GetScrollRange(this->Handle, SB_VERT, &nMin, &nMax);
512
515 }
516
522 void SetHorizontalScrollRange(double min, double max)
523 {
525 info.cbSize = sizeof(info);
526 info.fMask = SIF_RANGE | SIF_PAGE;
527 info.nMin = Dip::DipToPxX(min);
528 info.nMax = Dip::DipToPxX(max);
529 info.nPage = Dip::DipToPxX(this->ClientWidth);
530
531 SetScrollInfo(this->Handle, SB_HORZ, &info, true);
532 }
533
539 void SetVerticalScrollRange(double min, double max)
540 {
542 info.cbSize = sizeof(info);
543 info.fMask = SIF_RANGE | SIF_PAGE;
544 info.nMin = Dip::DipToPxY(min);
545 info.nMax = Dip::DipToPxY(max);
546 info.nPage = Dip::DipToPxY(this->ClientHeight);
547
548 SetScrollInfo(this->Handle, SB_VERT, &info, true);
549 }
550
555 {
557 info.cbSize = sizeof(info);
558 info.fMask = SIF_PAGE;
559 GetScrollInfo(this->Handle, SB_HORZ, &info);
560 return Dip::PxToDipX(info.nPage);
561 }
562
567 {
569 info.cbSize = sizeof(info);
570 info.fMask = SIF_PAGE;
571 GetScrollInfo(this->Handle, SB_VERT, &info);
572 return Dip::PxToDipY(info.nPage);
573 }
574
579 {
581 info.cbSize = sizeof(info);
582 info.fMask = SIF_PAGE;
583 info.nPage = Dip::DipToPxX(pageSize);
584 SetScrollInfo(this->Handle, SB_HORZ, &info, true);
585 }
586
591 {
593 info.cbSize = sizeof(info);
594 info.fMask = SIF_PAGE;
595 info.nPage = Dip::DipToPxY(pageSize);
596 SetScrollInfo(this->Handle, SB_VERT, &info, true);
597 }
598
603 {
604 if (_GetLayout() == nullptr) {
605 // 当未设置布局方式时滚动条和控件位置需要手动设置
606 // 将以下俩字段设为false确保xxxScrollLimit属性在未设置布局方式时仍可用
607 _horizontalScrollDisabled = false;
608 _verticalScrollDisabled = false;
609 return;
610 }
611
612 if (HorizontalScrollBar) {
613 double childRightmost = this->GetChildRightmost(true);
614
615 if (int(childRightmost - this->ClientWidth) > 0) {
616 _horizontalScrollDisabled = false;
618 SetHorizontalScrollRange(0, childRightmost);
619
620 // 当尺寸改变时确保子元素位置与滚动条同步
621 double pos = HorizontalScrollPos;
622 if (-this->GetInternalArrangeOffsetX() > pos) {
623 HorizontalScrollPos = pos;
624 }
625
626 } else {
627 HorizontalScrollPos = 0;
629 _horizontalScrollDisabled = true;
630 }
631 }
632
633 if (VerticalScrollBar) {
634 double childBottommost = this->GetChildBottommost(true);
635
636 if (int(childBottommost - this->ClientHeight) > 0) {
637 _verticalScrollDisabled = false;
639 SetVerticalScrollRange(0, childBottommost);
640
641 // 当尺寸改变时确保子元素位置与滚动条同步
642 double pos = VerticalScrollPos;
643 if (-this->GetInternalArrangeOffsetY() > pos) {
644 VerticalScrollPos = pos;
645 }
646
647 } else {
648 VerticalScrollPos = 0;
650 _verticalScrollDisabled = true;
651 }
652 }
653 }
654
659 {
660 VerticalScrollPos = 0;
661 }
662
667 {
668 VerticalScrollPos = VerticalScrollLimit;
669 }
670
675 {
676 HorizontalScrollPos = 0;
677 }
678
683 {
684 HorizontalScrollPos = HorizontalScrollLimit;
685 }
686
692 {
693 HorizontalScrollPos += offset;
694 }
695
701 {
702 VerticalScrollPos += offset;
703 }
704
705 private:
709 LayoutHost *_GetLayout()
710 {
711
712 auto layout = (_customLayout != nullptr) ? _customLayout : GetDefaultLayout();
713 return (layout != nullptr && layout->IsAssociated(this)) ? layout : nullptr;
714 }
715
719 void _MeasureAndArrangeWithoutLayout()
720 {
721 this->GetInternalArrangeOffsetX() = 0;
722 this->GetInternalArrangeOffsetY() = 0;
723
724 int childCount = this->GetChildLayoutCount();
725
726 for (int i = 0; i < childCount; ++i) {
727 // measure
728 UIElement &item = static_cast<UIElement &>(this->GetChildLayoutAt(i));
729 item.Measure(Size{INFINITY, INFINITY});
730 // arrange
731 Size desireSize = item.GetDesireSize();
732 sw::Rect itemRect = item.Rect;
733 Thickness itemMargin = item.Margin;
734 item.Arrange(sw::Rect{itemRect.left - itemMargin.left, itemRect.top - itemMargin.top, desireSize.width, desireSize.height});
735 }
736 }
737
741 void _MeasureAndArrangeWithoutResize(LayoutHost &layout, const Size &clientSize)
742 {
743 if (layout.IsAssociated(this)) {
744 layout.MeasureOverride(clientSize);
745 layout.ArrangeOverride(clientSize);
746 }
747 }
748 };
749
750 /*================================================================================*/
751
752 extern template class Layer<UIElement>;
753}
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:381
virtual void Arrange(const sw::Rect &finalPosition) override
安排元素位置
Definition Layer.h:370
virtual LayoutHost * GetDefaultLayout()
获取默认布局对象
Definition Layer.h:247
void SetVerticalScrollRange(double min, double max)
设置纵向滚动条的范围
Definition Layer.h:539
void ScrollToRight()
将水平滚动条移动到最右
Definition Layer.h:682
void UpdateScrollRange()
根据子元素更新滚动条范围,未设定布局方式时该函数无效
Definition Layer.h:602
virtual void ArrangeOverride(const Size &finalSize) override
安排子元素的位置,可重写该函数以实现自定义布局
Definition Layer.h:398
void SetHorizontalScrollPageSize(double pageSize)
设置水平滚动条滚动页面大小
Definition Layer.h:578
void ScrollToLeft()
将水平滚动条移动到最左
Definition Layer.h:674
void SetHorizontalScrollRange(double min, double max)
设置横向滚动条的范围
Definition Layer.h:522
void ScrollToBottom()
将垂直滚动条移动到底部
Definition Layer.h:666
void GetVerticalScrollRange(double &refMin, double &refMax)
获取纵向滚动条的范围
Definition Layer.h:508
virtual bool OnVerticalScroll(int event, int pos) override
接收到WM_VSCROLL时调用目标控件的该函数
Definition Layer.h:346
void SetVerticalScrollPageSize(double pageSize)
设置垂直滚动条滚动页面大小
Definition Layer.h:590
virtual void OnRoutedEvent(RoutedEventArgs &eventArgs, const RoutedEventHandler &handler) override
路由事件经过当前元素时调用该函数
Definition Layer.h:464
virtual void OnScroll(ScrollOrientation scrollbar, ScrollEvent event, double pos)
触发滚动条相关事件时调用该函数
Definition Layer.h:258
virtual bool RequestBringIntoView(const sw::Rect &screenRect) override
尝试将指定的矩形区域移动到可视区域内
Definition Layer.h:419
void ScrollToTop()
将垂直滚动条移动到顶部
Definition Layer.h:658
void GetHorizontalScrollRange(double &refMin, double &refMax)
获取横向滚动条的范围
Definition Layer.h:494
double GetVerticalScrollPageSize()
获取垂直滚动条滚动页面大小
Definition Layer.h:566
double GetHorizontalScrollPageSize()
获取水平滚动条滚动页面大小
Definition Layer.h:554
virtual bool OnHorizontalScroll(int event, int pos) override
接收到WM_HSCROLL时调用目标控件的该函数
Definition Layer.h:359
Definition Layer.h:12
用于托管元素的布局方式的对象类型,是所有布局方式类型的基类
Definition LayoutHost.h:11
void Associate(ILayout *obj)
设置关联的对象,每个LayoutHost只能关联一个对象
static StaticPropertyInitializer< T > Init()
获取静态属性初始化器
Definition Property.h:1523
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 RoutedEventArgs.h:134
表示相对于左上角的点坐标
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:154
窗口/面板滚动条滚动事件参数类型
Definition RoutedEventArgs.h:199
尺寸
Definition Size.h:15