1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 using System.Windows.API;
  9 
 10 namespace System.Windows
 11 {
 12     public nothrow ControlCreateParams& ScrollableControlControlCreateParams(ControlCreateParams& controlCreateParamsControl* child)
 13     {
 14         return controlCreateParams.SetWindowClassName("System.Windows.ScrollableControl").SetBackgroundColor(child->BackgroundColor());
 15     }
 16 
 17     public class ScrollableControlCreateParams
 18     {
 19         public nothrow ScrollableControlCreateParams(ControlCreateParams& controlCreateParams_Control* child_) : 
 20             controlCreateParams(controlCreateParams_)child(child_)
 21         {
 22         }
 23         public nothrow ScrollableControlCreateParams& Defaults()
 24         {
 25             return *this;
 26         }
 27         public ControlCreateParams& controlCreateParams;
 28         public Control* child;
 29     }
 30 
 31     public class ScrollableControl : Control
 32     {
 33         public ScrollableControl(Control* child_const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 34             base("System.Windows.ScrollableControl"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
 35             child_->BackgroundColor()"scrollableControl"locationsizedockanchors)container(this)child(child_)
 36             verticalScrollUnit(0)horizontalScrollUnit(0)vpage(0u)vpos(0)vmin(0)vmax(0)hpage(0u)hpos(0)hmin(0)hmax(0)
 37             verticalScrollBarShown(false)horizontalScrollBarShown(false)
 38         {
 39             container.AddChild(child);
 40         }
 41         public ScrollableControl(ScrollableControlCreateParams& createParams) : 
 42             base(createParams.controlCreateParams)container(this)child(createParams.child)
 43             verticalScrollUnit(0)horizontalScrollUnit(0)vpage(0u)vpos(0)vmin(0)vmax(0)hpage(0u)hpos(0)hmin(0)hmax(0)
 44             verticalScrollBarShown(false)horizontalScrollBarShown(false)
 45         {
 46             container.AddChild(child);
 47         }
 48         protected override nothrow bool IsDecoratorControl() const
 49         {
 50             return true;
 51         }
 52         protected override void OnLocationChanged()
 53         {
 54             base->OnLocationChanged();
 55             child->SetLocation(Point());
 56             child->SetSize(GetSize());
 57         }
 58         protected override void OnSizeChanged(uint windowState)
 59         {
 60             base->OnSizeChanged(windowState);
 61             child->SetLocation(Point());
 62             child->SetSize(GetSize());
 63         }
 64         protected override void OnChildSizeChanged(ControlEventArgs& args)
 65         {
 66             base->OnChildSizeChanged(args);
 67             ChildSizeOrContentSizeChanged(args);
 68         }
 69         protected override void OnChildContentChanged(ControlEventArgs& args)
 70         {
 71             base->OnChildContentChanged(args);
 72             hpos = 0;
 73             vpos = 0;
 74         }
 75         protected override void OnChildContentLocationChanged(ControlEventArgs& args)
 76         {
 77             base->OnChildContentLocationChanged(args);
 78             scrolledChild = args.control;
 79             Point childContentLocation = scrolledChild->ContentLocation();
 80             Pair<intint> scrollUnits = scrolledChild->GetScrollUnits();
 81             int verticalScrollUnit = scrollUnits.first;
 82             int horizontalScrollUnit = scrollUnits.second;
 83             vpos = childContentLocation.y / verticalScrollUnit;
 84             SetScrollInfo(Handle()ScrollBar.SB_VERTScrollInfoMask.SIF_POStrue0uvpos00);
 85             hpos = childContentLocation.x / horizontalScrollUnit;
 86             SetScrollInfo(Handle()ScrollBar.SB_HORZScrollInfoMask.SIF_POStrue0uhpos00);
 87         }
 88         private void ChildSizeOrContentSizeChanged(ControlEventArgs& args)
 89         {
 90             scrolledChild = args.control;
 91             Pair<intint> scrollUnits = scrolledChild->GetScrollUnits();
 92             verticalScrollUnit = scrollUnits.first;
 93             horizontalScrollUnit = scrollUnits.second;
 94             Size scrolledChildClientSize = scrolledChild->GetSize();
 95             Size scrolledChildContentSize = scrolledChild->ContentSize();
 96             if (scrolledChildContentSize.h > scrolledChildClientSize.h)
 97             {
 98                 vmin = 0;
 99                 vmax = 1;
100                 vpage = 1u;
101                 if (verticalScrollUnit > 0)
102                 {
103                     vmax = cast<int>(scrolledChildContentSize.h / verticalScrollUnit);
104                     vpage = cast<uint>(scrolledChildClientSize.h / verticalScrollUnit);
105                 }
106                 SetScrollInfo(Handle()ScrollBar.SB_VERTcast<ScrollInfoMask>(ScrollInfoMask.SIF_POS | ScrollInfoMask.SIF_PAGE | ScrollInfoMask.SIF_RANGE)true
107                     vpagevposvminvmax);
108                 ShowScrollBar(Handle()ScrollBar.SB_VERTtrue);
109                 verticalScrollBarShown = true;
110             }
111             else
112             {
113                 ShowScrollBar(Handle()ScrollBar.SB_VERTfalse);
114                 verticalScrollBarShown = false;
115             }
116             if (scrolledChildContentSize.w > scrolledChildClientSize.w)
117             {
118                 hmin = 0;
119                 hmax = 1;
120                 hpage = 1u;
121                 if (horizontalScrollUnit > 0)
122                 {
123                     hmax = cast<int>(scrolledChildContentSize.w / horizontalScrollUnit);
124                     hpage = cast<uint>(scrolledChildClientSize.w / horizontalScrollUnit);
125                 }
126                 SetScrollInfo(Handle()ScrollBar.SB_HORZcast<ScrollInfoMask>(ScrollInfoMask.SIF_POS | ScrollInfoMask.SIF_PAGE | ScrollInfoMask.SIF_RANGE)true
127                     hpagehposhminhmax);
128                 ShowScrollBar(Handle()ScrollBar.SB_HORZtrue);
129                 horizontalScrollBarShown = true;
130             }
131             else
132             {
133                 ShowScrollBar(Handle()ScrollBar.SB_HORZfalse);
134                 horizontalScrollBarShown = false;
135             }
136         }
137         protected override void OnChildContentSizeChanged(ControlEventArgs& args)
138         {
139             base->OnChildContentSizeChanged(args);
140             ChildSizeOrContentSizeChanged(args);
141         }
142         protected override void OnChildGotFocus(ControlEventArgs& args)
143         {
144             base->OnChildGotFocus(args);
145             Control* parentControl = ParentControl();
146             if (parentControl != null)
147             {
148                 parentControl->OnChildGotFocus(args);
149             }
150         }
151         protected override void OnChildLostFocus(ControlEventArgs& args)
152         {
153             base->OnChildLostFocus(args);
154             Control* parentControl = ParentControl();
155             if (parentControl != null)
156             {
157                 parentControl->OnChildLostFocus(args);
158             }
159         }
160         protected override void OnHScroll(ScrollEventArgs& args)
161         {
162             base->OnHScroll(args);
163             int trackPos;
164             GetScrollInfo(Handle()ScrollBar.SB_HORZhpagehposhminhmaxtrackPos);
165             int prevHPos = hpos;
166             switch (args.request)
167             {
168                 case SB_LINELEFT:
169                 {
170                     --hpos;
171                     break;
172                 }
173                 case SB_LINERIGHT:
174                 {
175                     ++hpos;
176                     break;
177                 }
178                 case SB_PAGELEFT:
179                 {
180                     hpos = hpos - cast<int>(hpage);
181                     break;
182                 }
183                 case SB_PAGERIGHT:
184                 {
185                     hpos = hpos + cast<int>(hpage);
186                     break;
187                 }
188                 case SB_THUMBTRACK:
189                 {
190                     hpos = trackPos;
191                     break;
192                 }
193             }
194             SetScrollInfo(Handle()ScrollBar.SB_HORZScrollInfoMask.SIF_POStrue0uhpos00);
195             GetScrollInfo(Handle()ScrollBar.SB_HORZhpagehposhminhmaxtrackPos);
196             if (prevHPos != hpos)
197             {
198                 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hposverticalScrollUnit * vpos));
199                 int xAmount = horizontalScrollUnit * (prevHPos - hpos);
200                 Rect updateRect = MakeUpdateRect(xAmount0);
201                 ScrollWindowEx(scrolledChild->Handle()xAmount0nullnullupdateRect);
202                 if (scrolledChild->IsDoubleBuffered())
203                 {
204                     scrolledChild->Invalidate();
205                 }
206             }
207         }
208         public override void ScrollLineDown()
209         {
210             int trackPos;
211             GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
212             int prevVPos = vpos;
213             if (vpos != vmax)
214             {
215                 ++vpos;
216                 SetScrollInfo(Handle()ScrollBar.SB_VERTScrollInfoMask.SIF_POStrue0uvpos00);
217                 GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
218                 if (prevVPos != vpos)
219                 {
220                     scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hposverticalScrollUnit * vpos));
221                     int yAmount = verticalScrollUnit * (prevVPos - vpos);
222                     Rect updateRect = MakeUpdateRect(0yAmount);
223                     ScrollWindowEx(scrolledChild->Handle()0yAmountnullnullupdateRect);
224                     if (scrolledChild->IsDoubleBuffered())
225                     {
226                         scrolledChild->Invalidate();
227                     }
228                 }
229             }
230         }
231         public override void ScrollLineUp()
232         {
233             int trackPos;
234             GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
235             int prevVPos = vpos;
236             if (vpos != vmin)
237             {
238                 --vpos;
239                 SetScrollInfo(Handle()ScrollBar.SB_VERTScrollInfoMask.SIF_POStrue0uvpos00);
240                 GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
241                 if (prevVPos != vpos)
242                 {
243                     scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hposverticalScrollUnit * vpos));
244                     int yAmount = verticalScrollUnit * (prevVPos - vpos);
245                     Rect updateRect = MakeUpdateRect(0yAmount);
246                     ScrollWindowEx(scrolledChild->Handle()0yAmountnullnullupdateRect);
247                     if (scrolledChild->IsDoubleBuffered())
248                     {
249                         scrolledChild->Invalidate();
250                     }
251                 }
252             }
253         }
254         protected override void OnVScroll(ScrollEventArgs& args)
255         {
256             base->OnVScroll(args);
257             int trackPos;
258             GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
259             int prevVPos = vpos;
260             switch (args.request)
261             {
262                 case SB_TOP:
263                 {
264                     vpos = vmin;
265                     break;
266                 }
267                 case SB_BOTTOM:
268                 {
269                     vpos = vmax;
270                     break;
271                 }
272                 case SB_LINEUP:
273                 {
274                     --vpos;
275                     break;
276                 }
277                 case SB_LINEDOWN:
278                 {
279                     ++vpos;
280                     break;
281                 }
282                 case SB_PAGEUP:
283                 {
284                     vpos = vpos - cast<int>(vpage);
285                     break;
286                 }
287                 case SB_PAGEDOWN:
288                 {
289                     vpos = vpos + cast<int>(vpage);
290                     break;
291                 }
292                 case SB_THUMBTRACK:
293                 {
294                     vpos = trackPos;
295                     break;
296                 }
297             }
298             SetScrollInfo(Handle()ScrollBar.SB_VERTScrollInfoMask.SIF_POStrue0uvpos00);
299             GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
300             if (prevVPos != vpos)
301             {
302                 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hposverticalScrollUnit * vpos));
303                 int yAmount = verticalScrollUnit * (prevVPos - vpos);
304                 Rect updateRect = MakeUpdateRect(0yAmount);
305                 ScrollWindowEx(scrolledChild->Handle()0yAmountnullnullupdateRect);
306                 if (scrolledChild->IsDoubleBuffered())
307                 {
308                     scrolledChild->Invalidate();
309                 }
310             }
311         }
312         protected override void OnMouseWheel(MouseWheelEventArgs& args)
313         {
314             base->OnMouseWheel(args);
315             if (!args.handled)
316             {
317                 if (verticalScrollBarShown)
318                 {
319                     int trackPos;
320                     GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
321                     int prevVPos = vpos;
322                     vpos = cast<int>(vpos - args.distance / (2.0 * verticalScrollUnit));
323                     SetScrollInfo(Handle()ScrollBar.SB_VERTScrollInfoMask.SIF_POStrue0uvpos00);
324                     GetScrollInfo(Handle()ScrollBar.SB_VERTvpagevposvminvmaxtrackPos);
325                     if (prevVPos != vpos)
326                     {
327                         Point contentLocation(horizontalScrollUnit * hposverticalScrollUnit * vpos);
328                         scrolledChild->SetContentLocationInternal(contentLocation);
329                         int yAmount = verticalScrollUnit * (prevVPos - vpos);
330                         Rect updateRect = MakeUpdateRect(0yAmount);
331                         ScrollWindowEx(scrolledChild->Handle()0yAmountnullnullupdateRect);
332                         if (scrolledChild->IsDoubleBuffered())
333                         {
334                             scrolledChild->Invalidate();
335                         }
336                     }
337                     args.handled = true;
338                 }
339             }
340         }
341         protected override void TranslateChildGraphics(Graphics& graphics)
342         {
343             int dx = -hpos * horizontalScrollUnit;
344             int dy = -vpos * verticalScrollUnit;
345             if (dx != 0 || dy != 0)
346             {
347                 graphics.TranslateTransformChecked(dxdy);
348             }
349         }
350         protected override void TranslateMousePos(Point& location)
351         {
352             int dx = hpos * horizontalScrollUnit;
353             int dy = vpos * verticalScrollUnit;
354             location.x = location.x + dx;
355             location.y = location.y + dy;
356         }
357         protected override void TranslateContentLocation(Point& location)
358         {
359             int dx = hpos * horizontalScrollUnit;
360             int dy = vpos * verticalScrollUnit;
361             location.x = location.x - dx;
362             location.y = location.y - dy;
363         }
364         internal override nothrow Control* GetFirstEnabledTabStopControl() const
365         {
366             return child->GetFirstEnabledTabStopControl();
367         }
368         internal override nothrow Control* GetLastEnabledTabStopControl() const
369         {
370             return child->GetLastEnabledTabStopControl();
371         }
372         private nothrow Rect MakeUpdateRect(int xAmountint yAmount)
373         {
374             Point loc(00);
375             Size size = scrolledChild->GetSize();
376             if (xAmount < 0)
377             {
378                 loc.x = size.w + xAmount;
379             }
380             if (xAmount != 0)
381             {
382                 size.w = Abs(xAmount);
383             }
384             if (yAmount < 0)
385             {
386                 loc.y = size.h + yAmount;
387             }
388             if (yAmount != 0)
389             {
390                 size.h = Abs(yAmount);
391             }
392             Rect updateRect(locsize);
393             updateRect.Inflate(horizontalScrollUnitverticalScrollUnit);
394             return updateRect;
395         }
396         private Container container;
397         private Control* child;
398         private Control* scrolledChild;
399         private int verticalScrollUnit;
400         private int horizontalScrollUnit;
401         private uint vpage;
402         private int vpos;
403         private int vmin;
404         private int vmax;
405         private uint hpage;
406         private int hpos;
407         private int hmin;
408         private int hmax;
409         private bool verticalScrollBarShown;
410         private bool horizontalScrollBarShown;
411     }
412 }