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