1 {
2 ATScrollBar for Delphi/Lazarus
3 Copyright (c) Alexey Torgashin (UVViewSoft)
4 License: MPL 2.0 or LGPL
5 
6 Features:
7 - fully supports owner-draw of all elements (arrows, backgnd, thumb, corner empty area)
8 - prop: border size
9 - prop: arrow mark size
10 - prop: size of corner empty area (for additional controls maybe)
11 - prop: kind of arrows (normal, both above, both below, no arrows)
12 
13 Mouse usage:
14 - click and holding mouse on arrows
15 - click and holding mouse on page-up (area above thumb) / page-down (area below thumb)
16 - dragging of thumb
17 }
18 
19 unit ATScrollBar;
20 
21 {$ifdef FPC}
22   {$mode delphi}
23 {$else}
24   {$define windows}
25 {$endif}
26 
27 interface
28 
29 uses
30   {$ifdef windows}
31   Windows, Messages,
32   {$endif}
33   {$ifdef FPC}
34   InterfaceBase,
35   LCLIntf,
36   LCLType,
37   {$endif}
38   Classes, Types, Graphics,
39   Controls, ExtCtrls, Forms,
40   ATCanvasPrimitives;
41 
42 type
43   TATScrollbarElemType = (
44     aseArrowUp,
45     aseArrowDown,
46     aseArrowLeft,
47     aseArrowRight,
48     aseBackAndThumbH,
49     aseBackAndThumbV,
50     aseScrollingAreaH,
51     aseScrollingAreaV,
52     aseCorner
53     );
54 
55 type
56   TATScrollbarArrowsStyle = (
57     asaArrowsNormal,
58     asaArrowsBelow,
59     asaArrowsAbove,
60     asaArrowsHidden
61     );
62 
63 type
64   TATScrollbarDrawEvent = procedure (Sender: TObject; AType: TATScrollbarElemType;
65     ACanvas: TCanvas; const ARect, ARect2: TRect; var ACanDraw: boolean) of object;
66 
67 type
68   PATScrollbarTheme = ^TATScrollbarTheme;
69   TATScrollbarTheme = record
70     ColorBG: TColor;
71     ColorBorder: TColor;
72     ColorThumbBorder: TColor;
73     ColorThumbFill: TColor;
74     ColorThumbFillOver: TColor;
75     ColorThumbFillPressed: TColor;
76     ColorThumbDecor: TColor;
77     ColorThumbDecor2: TColor;
78 
79     ColorArrowBorder: TColor;
80     ColorArrowFill: TColor;
81     ColorArrowFillOver: TColor;
82     ColorArrowFillPressed: TColor;
83 
84     ColorArrowSign: TColor;
85     ColorScrolled: TColor;
86 
87     InitialSize: integer;
88     ScalePercents: integer;
89     ArrowStyleH: TATScrollbarArrowsStyle;
90     ArrowStyleV: TATScrollbarArrowsStyle;
91     ArrowSize: integer;
92     ArrowLengthPercents: integer;
93     BorderSize: integer;
94     TimerInterval: integer;
95     DirectJumpOnClickPageUpDown: boolean;
96     ClickFocusesParentControl: boolean;
97 
98     MinSizeToShowThumb: integer;
99     ThumbMinSize: integer;
100     ThumbMarkerOffset: integer;
101     ThumbMarkerMinimalSize: integer;
102     ThumbMarkerDecorSize: integer;
103     ThumbMarkerDecorSpace: integer;
104     ThumbMarkerDecorDouble: boolean;
105     ThumbRoundedRect: boolean;
106   end;
107 
108 var
109   ATScrollbarTheme: TATScrollbarTheme;
110 
111 type
112   { TATScrollbar }
113 
114   TATScrollbar = class(TCustomControl)
115   private
116     FTimerMouseover: TTimer;
117 
118     {$ifndef FPC}
119     FOnMouseLeave: TNotifyEvent;
120     FOnMouseEnter: TNotifyEvent;
121     {$endif}
122 
123     FKind: TScrollBarKind;
124     FIndentCorner: Integer;
125     FTheme: PATScrollbarTheme;
126 
127     FPos: Int64;
128     FMin: Int64;
129     FMax: Int64;
130     FSmallChange: Int64;
131     FLargeChange: Int64;
132     FPageSize: Int64;
133     FDeltaOfThumb: Int64;
134 
135     //internal
136     FRectMain: TRect; //area for scrolling
137     FRectArrUp: TRect; //area for up or left arrow
138     FRectArrDown: TRect; //area for down or right arrow
139     FRectThumb: TRect; //area for scroll-thumb
140     FRectCorner: TRect;
141     FRectPageUp: TRect;
142     FRectPageDown: TRect;
143 
144     FBitmap: TBitmap;
145     FTimer: TTimer;
146     FOnChange: TNotifyEvent;
147     FOnOwnerDraw: TATScrollbarDrawEvent;
148 
149     //drag-drop
150     FMouseDown: boolean;
151     FMouseDragOffset: Integer;
152     FMouseDownOnUp,
153     FMouseDownOnDown,
154     FMouseDownOnThumb,
155     FMouseDownOnPageUp,
156     FMouseDownOnPageDown: boolean;
157 
158     {$ifndef FPC}
159     procedure CMMouseEnter(var msg: TMessage);
160       message CM_MOUSEENTER;
161     procedure CMMouseLeave(var msg: TMessage);
162       message CM_MOUSELEAVE;
163     {$endif}
164 
EffectiveRectSizenull165     function EffectiveRectSize: integer;
166     procedure TimerMouseoverTick(Sender: TObject);
167 
168     procedure DoPaintArrow(C: TCanvas; const R: TRect; AType: TATScrollbarElemType);
169     procedure DoPaintBackAndThumb(C: TCanvas);
170     procedure DoPaintBackScrolling(C: TCanvas);
171     procedure DoPaintTo(C: TCanvas);
172 
173     procedure DoPaintStd_Corner(C: TCanvas; const R: TRect);
174     procedure DoPaintStd_Back(C: TCanvas; const R: TRect);
175     procedure DoPaintStd_BackScrolling(C: TCanvas; const R: TRect);
176     procedure DoPaintStd_Arrow(C: TCanvas; R: TRect; AType: TATScrollbarElemType);
177     procedure DoPaintStd_Thumb(C: TCanvas; const R: TRect);
178 
IsHorznull179     function IsHorz: boolean; inline;
CoordToPosnull180     function CoordToPos(X, Y: Integer): Integer;
181     procedure DoUpdateThumbRect;
182     procedure DoUpdateCornerRect;
183     procedure DoUpdatePosOnDrag(X, Y: Integer);
184     procedure DoScrollBy(NDelta: Integer);
PosToCoordnull185     function PosToCoord(APos: Integer): Integer;
DoScalenull186     function DoScale(AValue: integer): integer;
187 
188     procedure TimerTimer(Sender: TObject);
189     procedure SetKind(AValue: TScrollBarKind);
190     procedure SetPos(AValue: Int64);
191     procedure SetMin(Value: Int64);
192     procedure SetMax(Value: Int64);
193     procedure SetPageSize(Value: Int64);
DoDrawEventnull194     function DoDrawEvent(AType: TATScrollbarElemType;
195       ACanvas: TCanvas; const ARect, ARect2: TRect): boolean;
196   public
197     constructor Create(AOnwer: TComponent); override;
198     destructor Destroy; override;
CanFocusnull199     function CanFocus: boolean; override;
200     property Theme: PATScrollbarTheme read FTheme write FTheme;
201     procedure Update; reintroduce;
202 
203   protected
204     {$ifdef FPC}
205      procedure MouseLeave; override;
206      procedure MouseEnter; override;
207     {$endif}
208     procedure Paint; override;
209     procedure Resize; override;
210     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
211     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
212     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
213     procedure Click; override;
214     {$ifndef FPC}
215     procedure DoMouseEnter; dynamic;
216     procedure DoMouseLeave; dynamic;
217     {$endif}
218     {$ifdef windows}
219     procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
220     {$endif}
221   published
222     {$ifndef FPC}
223     property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
224     property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
225     {$endif}
226 
227     property Align;
228     property Anchors;
229     {$ifdef FPC}
230     property BorderSpacing;
231     {$endif}
232     property Constraints;
233     property Enabled;
234     property DoubleBuffered;
235     property ParentShowHint;
236     property PopupMenu;
237     property ShowHint;
238     property Visible;
239 
240     property Position: Int64 read FPos write SetPos default 0;
241     property Min: Int64 read FMin write SetMin default 0;
242     property Max: Int64 read FMax write SetMax default 100;
243     property SmallChange: Int64 read FSmallChange write FSmallChange default 1;
244     property LargeChange: Int64 read FLargeChange write FLargeChange default 0;
245     property PageSize: Int64 read FPageSize write SetPageSize default 20;
246     property Kind: TScrollBarKind read FKind write SetKind default sbHorizontal;
247     property IndentCorner: Integer read FIndentCorner write FIndentCorner default 0;
248 
249     property OnChange: TNotifyEvent read FOnChange write FOnChange;
250     property OnMouseDown;
251     property OnMouseUp;
252     property OnOwnerDraw: TATScrollbarDrawEvent read FOnOwnerDraw write FOnOwnerDraw;
253     property OnContextPopup;
254     property OnResize;
255   end;
256 
257 implementation
258 
259 uses
260   SysUtils, Math;
261 
IsDoubleBufferedNeedednull262 function IsDoubleBufferedNeeded: boolean;
263 begin
264   {$ifdef FPC}
265   Result:= WidgetSet.GetLCLCapability(lcCanDrawOutsideOnPaint) = LCL_CAPABILITY_YES;
266   {$else}
267   Result:= true;
268   {$endif}
269 end;
270 
271 { TATScrollbar }
272 
273 procedure TATScrollbar.TimerMouseoverTick(Sender: TObject);
274 //timer is workaround for LCL issue, where MouseLeave not called
275 //if mouse leaves app window area (at least on Linux)
276 {$ifdef FPC}
277 var
278   Pnt: TPoint;
279 {$endif}
280 begin
281   {$ifdef FPC}
282   Pnt:= ScreenToClient(Mouse.CursorPos);
283   if not PtInRect(ClientRect, Pnt) then
284     MouseLeave;
285   {$endif}
286 end;
287 
288 {$ifdef FPC}
289 procedure TATScrollbar.MouseLeave;
290 begin
291   inherited;
292   FTimerMouseover.Enabled:= false;
293   //FOver:= false;
294   Invalidate;
295 end;
296 
297 procedure TATScrollbar.MouseEnter;
298 begin
299   inherited;
300   //FOver:= true;
301   Invalidate;
302   FTimerMouseover.Enabled:= true;
303 end;
304 {$endif}
305 
306 constructor TATScrollbar.Create(AOnwer: TComponent);
307 begin
308   inherited;
309 
310   Caption:= '';
311   {$ifdef FPC}
312   BorderStyle:= bsNone;
313   {$endif}
314   ControlStyle:= ControlStyle+[csOpaque];
315 
316   FKind:= sbHorizontal;
317   FIndentCorner:= 0;
318 
319   FTheme:= @ATScrollbarTheme;
320   Width:= 200;
321   Height:= FTheme.InitialSize;
322   Color:= FTheme^.ColorBG;
323 
324   DoubleBuffered:= IsDoubleBufferedNeeded;
325 
326   FMin:= 0;
327   FMax:= 100;
328   FSmallChange:= 1;
329   FLargeChange:= 0;
330   FPageSize:= 20;
331 
332   FBitmap:= TBitmap.Create;
333   FBitmap.PixelFormat:= pf24bit;
334   BitmapResize(FBitmap, 600, 50);
335 
336   FTimer:= TTimer.Create(Self);
337   FTimer.Enabled:= false;
338   FTimer.Interval:= 100;
339   FTimer.OnTimer:= TimerTimer;
340 
341   FTimerMouseover:= TTimer.Create(Self);
342   FTimerMouseover.Enabled:= false;
343   FTimerMouseover.Interval:= 1000;
344   FTimerMouseover.OnTimer:= TimerMouseoverTick;
345 
346   FMouseDown:= false;
347   FMouseDragOffset:= 0;
348 end;
349 
350 destructor TATScrollbar.Destroy;
351 begin
352   FTimer.Enabled:= false;
353   FreeAndNil(FTimer);
354   FreeAndNil(FBitmap);
355   inherited;
356 end;
357 
TATScrollbar.CanFocusnull358 function TATScrollbar.CanFocus: boolean;
359 begin
360   Result:= false;
361 end;
362 
363 procedure TATScrollbar.Update;
364 begin
365   if IsHorz then
366     Height:= DoScale(FTheme^.InitialSize)
367   else
368     Width:= DoScale(FTheme^.InitialSize);
369 
370   Invalidate;
371 end;
372 
TATScrollbar.DoScalenull373 function TATScrollbar.DoScale(AValue: integer): integer;
374 begin
375   Result:= AValue * FTheme^.ScalePercents div 100;
376 end;
377 
378 procedure TATScrollbar.Paint;
379 begin
380   if DoubleBuffered then
381   begin
382     if Assigned(FBitmap) then
383     begin
384       DoPaintTo(FBitmap.Canvas);
385       Canvas.CopyRect(ClientRect, FBitmap.Canvas, ClientRect);
386     end;
387   end
388   else
389     DoPaintTo(Canvas);
390 end;
391 
392 procedure TATScrollbar.DoPaintTo(C: TCanvas);
393 var
394   FSize: Integer;
395 begin
396   FRectMain:= ClientRect;
397   FRectArrUp:= Rect(0, 0, 0, 0);
398   FRectArrDown:= Rect(0, 0, 0, 0);
399 
400   DoUpdateCornerRect;
401   if not IsRectEmpty(FRectCorner) then
402     if DoDrawEvent(aseCorner, C, FRectCorner, FRectCorner) then
403       DoPaintStd_Corner(C, FRectCorner);
404 
405   C.Brush.Color:= ColorToRGB(FTheme^.ColorBorder);
406   C.FillRect(FRectMain);
407 
408   InflateRect(FRectMain,
409     -DoScale(FTheme^.BorderSize),
410     -DoScale(FTheme^.BorderSize)
411     );
412 
413   if IsHorz then
414   begin
415     //horz kind
416     FSize:= Math.Min(
417       FRectMain.Height * FTheme^.ArrowLengthPercents div 100,
418       FRectMain.Width div 2
419       );
420     case FTheme^.ArrowStyleH of
421       asaArrowsNormal:
422         begin
423           FRectArrUp:= Rect(FRectMain.Left, FRectMain.Top, FRectMain.Left+FSize, FRectMain.Bottom);
424           FRectArrDown:= Rect(FRectMain.Right-FSize, FRectMain.Top, FRectMain.Right, FRectMain.Bottom);
425           Inc(FRectMain.Left, FSize);
426           Dec(FRectMain.Right, FSize);
427         end;
428       asaArrowsBelow:
429         begin
430           FRectArrUp:= Rect(FRectMain.Left, FRectMain.Top, FRectMain.Left+FSize, FRectMain.Bottom);
431           FRectArrDown:= Rect(FRectMain.Left+FSize, FRectMain.Top, FRectMain.Left+2*FSize, FRectMain.Bottom);
432           Inc(FRectMain.Left, 2*FSize);
433         end;
434       asaArrowsAbove:
435         begin
436           FRectArrDown:= Rect(FRectMain.Right-FSize, FRectMain.Top, FRectMain.Right, FRectMain.Bottom);
437           FRectArrUp:= Rect(FRectMain.Right-2*FSize, FRectMain.Top, FRectMain.Right-FSize, FRectMain.Bottom);
438           Dec(FRectMain.Right, 2*FSize);
439         end;
440     end;
441     DoPaintArrow(C, FRectArrUp, aseArrowLeft);
442     DoPaintArrow(C, FRectArrDown, aseArrowRight);
443   end
444   else
445   begin
446     //vertical kind
447     FSize:= Math.Min(
448       FRectMain.Width * FTheme^.ArrowLengthPercents div 100,
449       FRectMain.Height div 2
450       );
451     case FTheme^.ArrowStyleV of
452       asaArrowsNormal:
453         begin
454           FRectArrUp:= Rect(FRectMain.Left, FRectMain.Top, FRectMain.Right, FRectMain.Top+FSize);
455           FRectArrDown:= Rect(FRectMain.Left, FRectMain.Bottom-FSize, FRectMain.Right, FRectMain.Bottom);
456           Inc(FRectMain.Top, FSize);
457           Dec(FRectMain.Bottom, FSize);
458         end;
459       asaArrowsBelow:
460         begin
461           FRectArrUp:= Rect(FRectMain.Left, FRectMain.Bottom-2*FSize, FRectMain.Right, FRectMain.Bottom-FSize);
462           FRectArrDown:= Rect(FRectMain.Left, FRectMain.Bottom-FSize, FRectMain.Right, FRectMain.Bottom);
463           Dec(FRectMain.Bottom, 2*FSize);
464         end;
465       asaArrowsAbove:
466         begin
467           FRectArrUp:= Rect(FRectMain.Left, FRectMain.Top, FRectMain.Right, FRectMain.Top+FSize);
468           FRectArrDown:= Rect(FRectMain.Left, FRectMain.Top+FSize, FRectMain.Right, FRectMain.Top+2*FSize);
469           Inc(FRectMain.Top, 2*FSize);
470         end;
471     end;
472     DoPaintArrow(C, FRectArrUp, aseArrowUp);
473     DoPaintArrow(C, FRectArrDown, aseArrowDown);
474   end;
475 
476   DoUpdateThumbRect;
477   DoPaintBackAndThumb(C);
478   DoPaintBackScrolling(C);
479 end;
480 
481 procedure TATScrollbar.DoPaintBackAndThumb(C: TCanvas);
482 var
483   Typ: TATScrollbarElemType;
484 begin
485   if IsHorz then
486     Typ:= aseBackAndThumbH
487   else
488     Typ:= aseBackAndThumbV;
489 
490   if DoDrawEvent(Typ, C, FRectMain, FRectThumb) then
491   begin
492     DoPaintStd_Back(C, FRectMain);
493     if not IsRectEmpty(FRectThumb) then
494       DoPaintStd_Thumb(C, FRectThumb);
495   end;
496 end;
497 
498 procedure TATScrollbar.DoPaintBackScrolling(C: TCanvas);
499 var
500   Typ: TATScrollbarElemType;
501 begin
502   if Theme^.DirectJumpOnClickPageUpDown then exit;
503 
504   if IsHorz then
505     Typ:= aseScrollingAreaH
506   else
507     Typ:= aseScrollingAreaV;
508 
509   if FMouseDown and FMouseDownOnPageUp then
510     if DoDrawEvent(Typ, C, FRectPageUp, FRectPageUp) then
511       DoPaintStd_BackScrolling(C, FRectPageUp);
512 
513   if FMouseDown and FMouseDownOnPageDown then
514     if DoDrawEvent(Typ, C, FRectPageDown, FRectPageDown) then
515       DoPaintStd_BackScrolling(C, FRectPageDown);
516 end;
517 
518 
519 procedure TATScrollbar.MouseDown(Button: TMouseButton; Shift: TShiftState;
520   X, Y: Integer);
521 var
522   ScrollVal: integer;
523 begin
524   inherited;
525 
526   FMouseDown:= Button=mbLeft;
527   FMouseDownOnThumb:= PtInRect(FRectThumb, Point(X, Y));
528   FMouseDownOnUp:= PtInRect(FRectArrUp, Point(X, Y));
529   FMouseDownOnDown:= PtInRect(FRectArrDown, Point(X, Y));
530   FMouseDownOnPageUp:= PtInRect(FRectPageUp, Point(X, Y));
531   FMouseDownOnPageDown:= PtInRect(FRectPageDown, Point(X, Y));
532 
533   Invalidate;
534 
535   if IsHorz then
536     FMouseDragOffset:= X-FRectThumb.Left
537   else
538     FMouseDragOffset:= Y-FRectThumb.Top;
539 
540   if FMouseDown then
541   begin
542     FTimer.Interval:= FTheme^.TimerInterval;
543 
544     if FMouseDownOnUp then
545     begin
546       DoScrollBy(-FSmallChange);
547       FTimer.Enabled:= true;
548     end
549     else
550     if FMouseDownOnDown then
551     begin
552       DoScrollBy(FSmallChange);
553       FTimer.Enabled:= true;
554     end
555     else
556     if FMouseDownOnPageUp or FMouseDownOnPageDown then
557     begin
558       if FTheme^.DirectJumpOnClickPageUpDown then
559       begin
560         Position:= Math.Min(FMax-FPageSize,
561                    Math.Max(FMin,
562                    CoordToPos(X, Y)));
563       end
564       else
565       begin
566         if FLargeChange>0 then
567           ScrollVal:= FLargeChange
568         else
569           ScrollVal:= FPageSize;
570 
571         if FMouseDownOnPageUp then
572           DoScrollBy(-ScrollVal)
573         else
574           DoScrollBy(ScrollVal);
575 
576         FTimer.Enabled:= true;
577       end;
578     end;
579   end;
580 end;
581 
582 procedure TATScrollbar.MouseUp(Button: TMouseButton; Shift: TShiftState;
583   X, Y: Integer);
584 begin
585   inherited;
586 
587   FMouseDown:= false;
588   FMouseDownOnThumb:= false;
589 
590   FMouseDownOnUp:= false;
591   FMouseDownOnDown:= false;
592 
593   FTimer.Enabled:= false;
594   Invalidate;
595 end;
596 
597 procedure TATScrollbar.Resize;
598 begin
599   inherited;
600 
601   if Assigned(FBitmap) then
602     BitmapResizeBySteps(FBitmap, Width, Height);
603 
604   Invalidate;
605 end;
606 
607 //needed to remove flickering on resize and mouse-over
608 {$ifdef windows}
609 procedure TATScrollbar.WMEraseBkgnd(var Message: TMessage);
610 begin
611   Message.Result:= 1;
612 end;
613 {$endif}
614 
615 {$ifndef FPC}
616 procedure TATScrollbar.CMMouseEnter(var msg: TMessage);
617 begin
618   DoMouseEnter;
619 end;
620 
621 procedure TATScrollbar.CMMouseLeave(var msg: TMessage);
622 begin
623   DoMouseLeave;
624 end;
625 
626 procedure TATScrollbar.DoMouseEnter;
627 begin
628   Invalidate;
629   if Assigned(FOnMouseEnter) then FOnMouseEnter(Self);
630 end;
631 
632 procedure TATScrollbar.DoMouseLeave;
633 begin
634   Invalidate;
635   if Assigned(FOnMouseLeave) then FOnMouseLeave(Self);
636 end;
637 {$endif}
638 
639 procedure TATScrollbar.Click;
640 var
641   Ctl: TWinControl;
642 begin
643   inherited;
644 
645   if Theme^.ClickFocusesParentControl then
646     if Parent is TWinControl then
647     begin
648       Ctl:= TWinControl(Parent);
649       if Ctl.Visible and Ctl.Enabled and Ctl.CanFocus then
650         Ctl.SetFocus;
651     end;
652 end;
653 
DoDrawEventnull654 function TATScrollbar.DoDrawEvent(AType: TATScrollbarElemType;
655   ACanvas: TCanvas; const ARect, ARect2: TRect): boolean;
656 begin
657   Result:= true;
658   if Assigned(FOnOwnerDraw) then
659     FOnOwnerDraw(Self, AType, ACanvas, ARect, ARect2, Result);
660 end;
661 
662 procedure TATScrollbar.SetKind(AValue: TScrollBarKind);
663 begin
664   if AValue=FKind then Exit;
665   FKind:= AValue;
666 
667   if IsHorz then
668   begin
669     Width:= 200;
670     Height:= FTheme^.InitialSize;
671   end
672   else
673   begin
674     Height:= 200;
675     Width:= FTheme^.InitialSize;
676   end;
677 end;
678 
679 procedure TATScrollbar.DoPaintArrow(C: TCanvas; const R: TRect;
680   AType: TATScrollbarElemType);
681 begin
682   if IsRectEmpty(R) then exit;
683   if DoDrawEvent(AType, C, R, R) then
684     DoPaintStd_Arrow(C, R, AType);
685 end;
686 
687 procedure TATScrollbar.DoPaintStd_Arrow(C: TCanvas; R: TRect;
688   AType: TATScrollbarElemType);
689 var
690   P: TPoint;
691   NSize: Integer;
692 begin
693   if IsRectEmpty(R) then exit;
694   C.Brush.Color:= ColorToRGB(FTheme^.ColorArrowBorder);
695   C.FillRect(R);
696 
697   InflateRect(R, -1, -1);
698   C.Brush.Color:= ColorToRGB(FTheme^.ColorArrowFill);
699   C.FillRect(R);
700 
701   if (FMouseDownOnUp and (AType in [aseArrowUp, aseArrowLeft])) or
702     (FMouseDownOnDown and (AType in [aseArrowDown, aseArrowRight])) then
703   begin
704     C.Brush.Color:= ColorToRGB(FTheme^.ColorArrowFillPressed);
705     C.FillRect(R);
706   end
707   else
708   begin
709     P:= Mouse.CursorPos;
710     P:= ScreenToClient(P);
711     if PtInRect(R,P) then
712     begin
713       C.Brush.Color:= ColorToRGB(FTheme^.ColorArrowFillOver);
714       C.FillRect(R);
715     end;
716   end;
717 
718   P:= CenterPoint(R);
719   NSize:= DoScale(FTheme^.ArrowSize);
720 
721   case AType of
722     aseArrowUp:
723       CanvasPaintTriangleUp(C, FTheme^.ColorArrowSign, P, NSize);
724     aseArrowDown:
725       CanvasPaintTriangleDown(C, FTheme^.ColorArrowSign, P, NSize);
726     aseArrowLeft:
727       CanvasPaintTriangleLeft(C, FTheme^.ColorArrowSign, P, NSize);
728     aseArrowRight:
729       CanvasPaintTriangleRight(C, FTheme^.ColorArrowSign, P, NSize);
730     else
731       Exit;
732   end;
733 end;
734 
IsHorznull735 function TATScrollbar.IsHorz: boolean;
736 begin
737   Result:= FKind=sbHorizontal;
738 end;
739 
TATScrollbar.EffectiveRectSizenull740 function TATScrollbar.EffectiveRectSize: integer;
741 begin
742   if IsHorz then
743     Result:= FRectMain.Width
744   else
745     Result:= FRectMain.Height;
746 
747   if FDeltaOfThumb<0 then
748     Inc(Result, FDeltaOfThumb);
749 
750   if Result<1 then
751     Result:= 1;
752 end;
753 
TATScrollbar.PosToCoordnull754 function TATScrollbar.PosToCoord(APos: Integer): Integer;
755 var
756   N0: Integer;
757 begin
758   if IsHorz then
759   begin
760     N0:= FRectMain.Left;
761   end
762   else
763   begin
764     N0:= FRectMain.Top;
765   end;
766   Result:= N0 + (APos-FMin) * EffectiveRectSize div Math.Max(1, FMax-FMin);
767 end;
768 
769 procedure TATScrollbar.DoUpdateThumbRect;
770 var
771   R: TRect;
772   NMin: integer;
773 begin
774   FRectThumb:= Rect(0, 0, 0, 0);
775   FRectPageUp:= Rect(0, 0, 0, 0);
776   FRectPageDown:= Rect(0, 0, 0, 0);
777   NMin:= FTheme^.ThumbMinSize;
778 
779   if IsHorz then
780   begin
781     if FRectMain.Width<FTheme^.MinSizeToShowThumb then Exit;
782     R.Top:= FRectMain.Top;
783     R.Bottom:= FRectMain.Bottom;
784     R.Left:= PosToCoord(FPos);
785     R.Right:= PosToCoord(FPos+FPageSize);
786     FDeltaOfThumb:= R.Right-R.Left-NMin;
787     R.Left:= Math.Min(R.Left, FRectMain.Right-NMin);
788     R.Right:= Math.Max(R.Right, R.Left+NMin);
789     R.Right:= Math.Min(R.Right, FRectMain.Right);
790   end
791   else
792   begin
793     if FRectMain.Height<FTheme^.MinSizeToShowThumb then Exit;
794     R.Left:= FRectMain.Left;
795     R.Right:= FRectMain.Right;
796     R.Top:= PosToCoord(FPos);
797     R.Bottom:= PosToCoord(FPos+FPageSize);
798     FDeltaOfThumb:= R.Bottom-R.Top-NMin;
799     R.Top:= Math.Min(R.Top, FRectMain.Bottom-NMin);
800     R.Bottom:= Math.Max(R.Bottom, R.Top+NMin);
801     R.Bottom:= Math.Min(R.Bottom, FRectMain.Bottom);
802   end;
803   FRectThumb:= R;
804 
805   if IsHorz then
806   begin
807     FRectPageUp:= Rect(FRectMain.Left, FRectMain.Top, FRectThumb.Left, FRectMain.Bottom);
808     FRectPageDown:= Rect(FRectThumb.Right, FRectMain.Top, FRectMain.Right, FRectMain.Bottom);
809   end
810   else
811   begin
812     FRectPageUp:= Rect(FRectMain.Left, FRectMain.Top, FRectMain.Right, FRectThumb.Top);
813     FRectPageDown:= Rect(FRectMain.Left, FRectThumb.Bottom, FRectMain.Right, FRectMain.Bottom);
814   end;
815 end;
816 
817 procedure TATScrollbar.DoPaintStd_Thumb(C: TCanvas; const R: TRect);
818   //
819   procedure PaintMarkerHorz(X: integer; NDecorSize, NDecorSpace, NOffset, NInc: integer);
820   var
821     i: integer;
822   begin
823     for i:= 0 to NDecorSize-1 do
824     begin
825       C.MoveTo(X-NDecorSpace*i + NInc, R.Top+NOffset);
826       C.LineTo(X-NDecorSpace*i + NInc, R.Bottom-NOffset);
827       if i>0 then
828       begin
829         C.MoveTo(X+NDecorSpace*i + NInc, R.Top+NOffset);
830         C.LineTo(X+NDecorSpace*i + NInc, R.Bottom-NOffset);
831       end;
832     end;
833   end;
834   //
835   procedure PaintMarkerVert(Y: integer; NDecorSize, NDecorSpace, NOffset, NInc: integer);
836   var
837     i: integer;
838   begin
839     for i:= 0 to NDecorSize-1 do
840     begin
841       C.MoveTo(R.Left+NOffset, Y-NDecorSpace*i + NInc);
842       C.LineTo(R.Right-NOffset, Y-NDecorSpace*i + NInc);
843       if i>0 then
844       begin
845         C.MoveTo(R.Left+NOffset, Y+NDecorSpace*i + NInc);
846         C.LineTo(R.Right-NOffset, Y+NDecorSpace*i + NInc);
847       end;
848     end;
849   end;
850   //
851 var
852   P: TPoint;
853   NColorFill, NColorBorder, NColorBack: TColor;
854   NColorThumbDecor1, NColorThumbDecor2: TColor;
855   NOffset, NDecorSize, NDecorSpace: integer;
856 begin
857   NColorFill:= ColorToRGB(FTheme^.ColorThumbFill);
858   NColorBorder:= ColorToRGB(FTheme^.ColorThumbBorder);
859   NColorBack:= FTheme^.ColorBG;
860 
861   NColorThumbDecor1:= ColorToRGB(FTheme^.ColorThumbDecor);
862   NColorThumbDecor2:= ColorToRGB(FTheme^.ColorThumbDecor2);
863 
864   NOffset:= DoScale(FTheme^.ThumbMarkerOffset);
865   NDecorSize:= FTheme^.ThumbMarkerDecorSize;
866   NDecorSpace:= DoScale(FTheme^.ThumbMarkerDecorSpace);
867 
868   {
869   if NBorderSize>0 then
870   begin
871     if IsHorz then
872     begin
873       Inc(R.Top, NBorderSize);
874       Dec(R.Bottom, NBorderSize);
875     end
876     else
877     begin
878       Inc(R.Left, NBorderSize);
879       Dec(R.Right, NBorderSize);
880     end;
881   end;
882   }
883 
884   if FMouseDownOnThumb then
885     NColorFill:= ColorToRGB(FTheme^.ColorThumbFillPressed)
886   else
887   begin
888     P:= Mouse.CursorPos;
889     P:= ScreenToClient(P);
890     if PtInRect(R, P) then
891       NColorFill:= ColorToRGB(FTheme^.ColorThumbFillOver);
892   end;
893 
894   C.Brush.Color:= NColorFill;
895   C.Pen.Color:= NColorBorder;
896   C.Rectangle(R);
897 
898   if FTheme^.ThumbRoundedRect then
899     CanvasPaintRoundedCorners(
900       C, R,
901       [acckLeftTop, acckRightTop, acckLeftBottom, acckRightBottom],
902       NColorBack,
903       NColorBorder,
904       NColorFill);
905 
906   if IsHorz then
907   begin
908     if R.Width>FTheme^.ThumbMarkerMinimalSize then
909     begin
910       P:= CenterPoint(R);
911       if FTheme^.ThumbMarkerDecorDouble then
912         Inc(P.X);
913 
914       C.Pen.Color:= NColorThumbDecor1;
915       PaintMarkerHorz(P.X, NDecorSize, NDecorSpace, NOffset, 0);
916 
917       if FTheme^.ThumbMarkerDecorDouble then
918       begin
919         C.Pen.Color:= NColorThumbDecor2;
920         PaintMarkerHorz(P.X, NDecorSize, NDecorSpace, NOffset, -1);
921       end;
922     end;
923   end
924   else
925   begin
926     if R.Height>FTheme^.ThumbMarkerMinimalSize then
927     begin
928       P:= CenterPoint(R);
929       if FTheme^.ThumbMarkerDecorDouble then
930         Inc(P.Y);
931 
932       C.Pen.Color:= NColorThumbDecor1;
933       PaintMarkerVert(P.Y, NDecorSize, NDecorSpace, NOffset, 0);
934 
935       if FTheme^.ThumbMarkerDecorDouble then
936       begin
937         C.Pen.Color:= NColorThumbDecor2;
938         PaintMarkerVert(P.Y, NDecorSize, NDecorSpace, NOffset, -1);
939       end;
940     end;
941   end;
942 end;
943 
944 
945 procedure TATScrollbar.SetMax(Value: Int64);
946 begin
947   if FMax<>Value then
948   begin
949     FMax:= Value;
950     FPos:= Math.Min(FPos, FMax);
951     Invalidate;
952   end;
953 end;
954 
955 procedure TATScrollbar.SetMin(Value: Int64);
956 begin
957   if FMin<>Value then
958   begin
959     FMin:= Value;
960     FPos:= Math.Max(FPos, FMin);
961     Invalidate;
962   end;
963 end;
964 
965 procedure TATScrollbar.SetPageSize(Value: Int64);
966 begin
967   if FPageSize<>Value then
968   begin
969     FPageSize:= Value;
970     Invalidate;
971   end;
972 end;
973 
974 procedure TATScrollbar.SetPos(AValue: Int64);
975 begin
976   if AValue>FMax then
977     AValue:= FMax;
978   if AValue<FMin then
979     AValue:= FMin;
980 
981   if FPos<>AValue then
982   begin
983     FPos:= AValue;
984 
985     {$ifdef windows}
986     Repaint; //only Invalidate is not ok, it delays painting on big files
987     {$else}
988     Invalidate;
989     {$endif}
990 
991     if Assigned(FOnChange) then
992       FOnChange(Self);
993   end;
994 end;
995 
996 procedure TATScrollbar.MouseMove(Shift: TShiftState; X, Y: Integer);
997 begin
998   inherited;
999   Invalidate;
1000 
1001   if FMouseDownOnThumb then
1002   begin
1003     DoUpdatePosOnDrag(X, Y);
1004   end;
1005 end;
1006 
CoordToPosnull1007 function TATScrollbar.CoordToPos(X, Y: Integer): Integer;
1008 begin
1009   if IsHorz then
1010     Result:= FMin + (X-FRectMain.Left) * (FMax-FMin) div EffectiveRectSize
1011   else
1012     Result:= FMin + (Y-FRectMain.Top) * (FMax-FMin) div EffectiveRectSize;
1013 end;
1014 
1015 procedure TATScrollbar.DoUpdatePosOnDrag(X, Y: Integer);
1016 var
1017   N: Integer;
1018 begin
1019   N:= CoordToPos(
1020     X-FMouseDragOffset,
1021     Y-FMouseDragOffset);
1022   N:= Math.Max(N, FMin);
1023   N:= Math.Min(N, FMax-FPageSize);
1024   SetPos(N);
1025 end;
1026 
1027 procedure TATScrollbar.DoScrollBy(NDelta: Integer);
1028 var
1029   N: Integer;
1030 begin
1031   N:= FPos;
1032   Inc(N, NDelta);
1033   if (NDelta>0) then
1034     N:= Math.Min(N, FMax-FPageSize);
1035   SetPos(N);
1036 end;
1037 
1038 procedure TATScrollbar.TimerTimer(Sender: TObject);
1039 var
1040   P: TPoint;
1041 begin
1042   P:= Mouse.CursorPos;
1043   P:= ScreenToClient(P);
1044 
1045   if FMouseDownOnDown and PtInRect(FRectArrDown, P) then
1046     DoScrollBy(FSmallChange)
1047   else
1048   if FMouseDownOnUp and PtInRect(FRectArrUp, P) then
1049     DoScrollBy(-FSmallChange)
1050   else
1051   if FMouseDownOnPageDown and PtInRect(FRectPageDown, P) then
1052     DoScrollBy(FPageSize)
1053   else
1054   if FMouseDownOnPageUp and PtInRect(FRectPageUp, P) then
1055     DoScrollBy(-FPageSize);
1056 end;
1057 
1058 procedure TATScrollbar.DoPaintStd_Corner(C: TCanvas; const R: TRect);
1059 begin
1060   if IsRectEmpty(R) then exit;
1061   C.Brush.Color:= ColorToRGB(FTheme^.ColorBG);
1062   C.FillRect(R);
1063 end;
1064 
1065 procedure TATScrollbar.DoPaintStd_Back(C: TCanvas; const R: TRect);
1066 begin
1067   if IsRectEmpty(R) then exit;
1068   C.Brush.Color:= ColorToRGB(FTheme^.ColorBG);
1069   C.FillRect(R);
1070 end;
1071 
1072 procedure TATScrollbar.DoPaintStd_BackScrolling(C: TCanvas; const R: TRect);
1073 begin
1074   if IsRectEmpty(R) then exit;
1075   C.Brush.Color:= ColorToRGB(FTheme^.ColorScrolled);
1076   C.FillRect(R);
1077 end;
1078 
1079 procedure TATScrollbar.DoUpdateCornerRect;
1080 var
1081   w, h, Delta: integer;
1082 begin
1083   w:= Width;
1084   h:= Height;
1085   FRectCorner:= Rect(0, 0, 0, 0);
1086 
1087   if IsHorz then
1088     Delta:= FIndentCorner * h div 100
1089   else
1090     Delta:= FIndentCorner * w div 100;
1091 
1092   if IsHorz then
1093   begin
1094     if Delta>0 then
1095     begin
1096       FRectCorner:= Rect(w-Delta, 0, w, h);
1097       Dec(FRectMain.Right, Delta);
1098     end
1099     else
1100     if Delta<0 then
1101     begin
1102       FRectCorner:= Rect(0, 0, Abs(Delta), h);
1103       Inc(FRectMain.Left, Abs(Delta));
1104     end;
1105   end
1106   else
1107   begin
1108     if Delta>0 then
1109     begin
1110       FRectCorner:= Rect(0, h-Delta, w, h);
1111       Dec(FRectMain.Bottom, Delta);
1112     end
1113     else
1114     if Delta<0 then
1115     begin
1116       FRectCorner:= Rect(0, 0, w, Abs(Delta));
1117       Inc(FRectMain.Top, Abs(Delta));
1118     end;
1119   end;
1120 end;
1121 
1122 initialization
1123 
1124   with ATScrollbarTheme do
1125   begin
1126     ColorBG:= $d0d0d0;
1127     ColorBorder:= clLtGray;
1128     ColorThumbBorder:= $808080;
1129     ColorThumbFill:= $c0c0c0;
1130     ColorThumbFillOver:= $d0d0d0;
1131     ColorThumbFillPressed:= $e0c0c0;
1132     ColorThumbDecor:= ColorThumbBorder;
1133     ColorThumbDecor2:= clWhite;
1134 
1135     ColorArrowBorder:= $808080;
1136     ColorArrowFill:= $c0c0c0;
1137     ColorArrowFillOver:= $d0d0d0;
1138     ColorArrowFillPressed:= $e0a0a0;
1139 
1140     ColorArrowSign:= $404040;
1141     ColorScrolled:= $d0b0b0;
1142 
1143     InitialSize:= 16;
1144     ScalePercents:= 100;
1145     ArrowStyleH:= asaArrowsNormal;
1146     ArrowStyleV:= asaArrowsNormal;
1147     ArrowSize:= 2;
1148     ArrowLengthPercents:= 100;
1149     BorderSize:= 0;
1150     TimerInterval:= 200;
1151     DirectJumpOnClickPageUpDown:= false;
1152     ClickFocusesParentControl:= true;
1153 
1154     MinSizeToShowThumb:= 10;
1155     ThumbMinSize:= 8;
1156     ThumbMarkerOffset:= 3;
1157     ThumbMarkerMinimalSize:= 20;
1158     ThumbMarkerDecorSize:= 2;
1159     ThumbMarkerDecorSpace:= 2;
1160     ThumbMarkerDecorDouble:= false;
1161     ThumbRoundedRect:= {$ifdef darwin} false {$else} true {$endif};
1162   end;
1163 
1164 end.
1165