Browse Source

scrollbars: delegate mouse events

mattias 5 months ago
parent
commit
275123235e

+ 2 - 2
demo/ScrollBox/MainUnit.lfm

@@ -6,9 +6,9 @@ object MainForm: TMainForm
   FormHeight = 255
   FormHeight = 255
   Stylesheet.Strings = (
   Stylesheet.Strings = (
     'div {'
     'div {'
-    '  padding: 3px; '
+    '  padding: 5px; '
     '  border: 2px solid black; '
     '  border: 2px solid black; '
-    '  margin: 6px;'
+    '  margin: 8px;'
     '}'
     '}'
   )
   )
   OnCreate = MainFormCreate
   OnCreate = MainFormCreate

+ 1 - 0
demo/ScrollBox/MainUnit.pas

@@ -34,6 +34,7 @@ implementation
 procedure TMainForm.MainFormCreate(Sender: TObject);
 procedure TMainForm.MainFormCreate(Sender: TObject);
 begin
 begin
   Div1.Style:='overflow: auto; width: 200px; height: 50px;';
   Div1.Style:='overflow: auto; width: 200px; height: 50px;';
+
   Label1.Caption:='Fresnel is a visual component library based on CSS and custom drawn components.';
   Label1.Caption:='Fresnel is a visual component library based on CSS and custom drawn components.';
 end;
 end;
 
 

+ 174 - 105
src/base/fresnel.dom.pas

@@ -26,7 +26,7 @@ interface
 uses
 uses
   Classes, SysUtils, Math, FPImage,
   Classes, SysUtils, Math, FPImage,
   {$IF FPC_FULLVERSION>30300}
   {$IF FPC_FULLVERSION>30300}
-  sortbase,
+  sortbase, System.UITypes,
   {$ENDIF}
   {$ENDIF}
   fpCSSTree, fpCSSResParser, fpCSSResolver, fpCSSScanner, fpCSSParser, FCL.Events,
   fpCSSTree, fpCSSResParser, fpCSSResolver, fpCSSScanner, fpCSSParser, FCL.Events,
   Fresnel.Classes, Fresnel.Events;
   Fresnel.Classes, Fresnel.Events;
@@ -1245,14 +1245,23 @@ type
   private
   private
     FElement: TFresnelElement;
     FElement: TFresnelElement;
     FHorizontal: boolean;
     FHorizontal: boolean;
+  public type
+    {$ScopedEnums on}
+    THit = (hNone, hButtonLT, hButtonRB, hThumb, hTrackLT, hTrackRB);
+    {$ScopedEnums off}
   public
   public
-    Box: TFresnelRect;
-    Position: TFresnelLength; // ScrollLeft/Top
+    Position: TFresnelLength; // ScrollLeft/Top, When direction=rtl: Position=Size-Page+ScrollLeft
+                              // can be negative or bigger than Size
     Size: TFresnelLength; // ScrollWidth/Height
     Size: TFresnelLength; // ScrollWidth/Height
     Page: TFresnelLength; // ClientWidth/Height
     Page: TFresnelLength; // ClientWidth/Height
     Color: TFPColor;
     Color: TFPColor;
+    Box: TFresnelRect; // relative to layoutnode parent's RenderedContentBox
+    MouseDownHit: THit;
+    MouseDownPos: TFresnelLength;
     constructor Create(TheElement: TFresnelElement; aHorizontal: boolean);
     constructor Create(TheElement: TFresnelElement; aHorizontal: boolean);
     destructor Destroy; override;
     destructor Destroy; override;
+    function GetHit(const X, Y: TFresnelLength; out aPosition: TFresnelLength): THit; virtual; abstract;
+    procedure MouseHandler(WSData: TFresnelMouseEventInit; MouseEventId: TEventID); virtual; abstract;
     property Element: TFresnelElement read FElement;
     property Element: TFresnelElement read FElement;
     property Horizontal: boolean read FHorizontal;
     property Horizontal: boolean read FHorizontal;
   end;
   end;
@@ -1667,7 +1676,8 @@ type
     function GetScrollbarsWidth(IsHorizontal: boolean): TFresnelLength; virtual;
     function GetScrollbarsWidth(IsHorizontal: boolean): TFresnelLength; virtual;
     function GetVPLength(l: TFresnelViewportLength): TFresnelLength; virtual;
     function GetVPLength(l: TFresnelViewportLength): TFresnelLength; virtual;
     function GetWidth: TFresnelLength; virtual;
     function GetWidth: TFresnelLength; virtual;
-    procedure FPOObservedChanged(ASender: TObject; {%H-}Operation: TFPObservedOperation; {%H-}Data: Pointer); override;
+    procedure FPOObservedChanged(ASender: TObject; {%H-}Operation: TFPObservedOperation;
+      {%H-}Data: Pointer); override;
     procedure InitResolver; virtual;
     procedure InitResolver; virtual;
     procedure UpdateResolverStylesheet; virtual;
     procedure UpdateResolverStylesheet; virtual;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@@ -1682,6 +1692,15 @@ type
     procedure StylesheetChanged; virtual;
     procedure StylesheetChanged; virtual;
     function TrySetFocusControl(aControl : TFresnelElement) : Boolean;
     function TrySetFocusControl(aControl : TFresnelElement) : Boolean;
   public type
   public type
+      {$ScopedEnums on}
+      THit = (
+        hNone,
+        hElement,
+        hScrollbarHorz,
+        hScrollbarVert
+        );
+      {$ScopedEnums off}
+      THits = set of THit;
 
 
       { TBubbleMouseClickEvent }
       { TBubbleMouseClickEvent }
 
 
@@ -1748,6 +1767,9 @@ type
     class function GetCSSTypeStyle: TCSSString; override;
     class function GetCSSTypeStyle: TCSSString; override;
     function GetElementAt(const x, y: TFresnelLength): TFresnelElement; virtual;
     function GetElementAt(const x, y: TFresnelLength): TFresnelElement; virtual;
     function GetElementsAt(const x, y: TFresnelLength): TFresnelElementArray; virtual;
     function GetElementsAt(const x, y: TFresnelLength): TFresnelElementArray; virtual;
+    // returns in stacking order top to bottom aka child to parents
+    procedure GetElementsAt(const x, y: TFresnelLength; OnlyLast: boolean;
+      out Arr: TFresnelElementArray; out Hit: THit); virtual;
     function ContentToPagePos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content box of El to content of viewport
     function ContentToPagePos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content box of El to content of viewport
     function ContentToPagePos(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; overload; // content box of El to content of viewport
     function ContentToPagePos(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; overload; // content box of El to content of viewport
     function PageToContentPos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content of viewport to content box of El
     function PageToContentPos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content of viewport to content box of El
@@ -6719,7 +6741,7 @@ begin
   try
   try
     if assigned(FFocusedElement) then
     if assigned(FFocusedElement) then
       begin
       begin
-      // Blur does not bubble;
+      // Blur does not bubble
       lEvt:=TFresnelFocusEvent.Create(FFocusedElement,evtBlur);
       lEvt:=TFresnelFocusEvent.Create(FFocusedElement,evtBlur);
       lEvt.Related:=aControl;
       lEvt.Related:=aControl;
       FFocusedElement.DispatchEvent(lEvt);
       FFocusedElement.DispatchEvent(lEvt);
@@ -6733,7 +6755,7 @@ begin
     FFocusedElement:=aControl;
     FFocusedElement:=aControl;
     if assigned(FFocusedElement) then
     if assigned(FFocusedElement) then
       begin
       begin
-      // Focus does not bubble;
+      // Focus does not bubble
       lEvt:=TFresnelFocusEvent.Create(FFocusedElement,evtFocus);
       lEvt:=TFresnelFocusEvent.Create(FFocusedElement,evtFocus);
       lEvt.Related:=lOldElement;
       lEvt.Related:=lOldElement;
       FFocusedElement.DispatchEvent(levt);
       FFocusedElement.DispatchEvent(levt);
@@ -6851,8 +6873,8 @@ begin
   DomChanged;
   DomChanged;
 end;
 end;
 
 
-procedure TFresnelViewport.SetScrollbarsThinWidth(IsHorizontal: boolean; const AValue: TFresnelLength
-  );
+procedure TFresnelViewport.SetScrollbarsThinWidth(IsHorizontal: boolean;
+  const AValue: TFresnelLength);
 begin
 begin
   if FScrollbarThinWidth[IsHorizontal]=AValue then exit;
   if FScrollbarThinWidth[IsHorizontal]=AValue then exit;
   FScrollbarThinWidth[IsHorizontal]:=AValue;
   FScrollbarThinWidth[IsHorizontal]:=AValue;
@@ -7022,65 +7044,107 @@ end;
 
 
 function TFresnelViewport.GetElementAt(const x, y: TFresnelLength
 function TFresnelViewport.GetElementAt(const x, y: TFresnelLength
   ): TFresnelElement;
   ): TFresnelElement;
-
-  function Check(Node: TFresnelLayoutNode; const dx, dy: TFresnelLength): TFresnelElement;
-  var
-    El: TFresnelElement;
-    i: Integer;
-    BorderBox: TFresnelRect;
-    aContentOffset: TFresnelPoint;
-  begin
-    Result:=nil;
-    if Node=nil then exit;
-    El:=Node.Element;
-    if not El.Rendered then exit;
-    if Node.NodeCount>0 then begin
-      aContentOffset:=El.RenderedContentBox.TopLeft;
-      for i:=Node.NodeCount-1 downto 0 do
-      begin
-        Result:=Check(Node.Nodes[i],dx+aContentOffset.X,dy+aContentOffset.Y);
-        if Result<>nil then exit;
-      end;
-    end;
-    BorderBox:=El.RenderedBorderBox;
-    if BorderBox.Contains(x-dx,y-dy) then
-      Result:=El
-    else
-      Result:=nil;
-  end;
-
+var
+  Arr: TFresnelElementArray;
+  Hit: THit;
 begin
 begin
-  Result:=Check(LayoutNode,0,0);
+  GetElementsAt(x,y,true,Arr,Hit);
+  if Arr<>nil then
+    Result:=Arr[0]
+  else
+    Result:=nil;
 end;
 end;
 
 
 function TFresnelViewport.GetElementsAt(const x, y: TFresnelLength
 function TFresnelViewport.GetElementsAt(const x, y: TFresnelLength
   ): TFresnelElementArray;
   ): TFresnelElementArray;
+var
+  Hit: THit;
+begin
+  GetElementsAt(x,y,false,Result,Hit);
+end;
+
+procedure TFresnelViewport.GetElementsAt(const x, y: TFresnelLength; OnlyLast: boolean; out
+  Arr: TFresnelElementArray; out Hit: THit);
 
 
-  procedure Check(Node: TFresnelLayoutNode; const dx, dy: TFresnelLength);
+  function Check(Node: TFresnelLayoutNode; const dx, dy: TFresnelLength): boolean;
   var
   var
     El: TFresnelElement;
     El: TFresnelElement;
     i: Integer;
     i: Integer;
-    BorderBox: TFresnelRect;
+    BorderBox, r: TFresnelRect;
     aContentOffset: TFresnelPoint;
     aContentOffset: TFresnelPoint;
+    HitX, HitY: TFresnelLength;
+    aBar: TFresnelScrollBar;
+    ClipHorizontal, ClipVertical: Boolean;
   begin
   begin
+    Result:=false;
     if Node=nil then exit;
     if Node=nil then exit;
     El:=Node.Element;
     El:=Node.Element;
     if not El.Rendered then exit;
     if not El.Rendered then exit;
+
+    HitX:=x-dx;
+    HitY:=y-dy;
+
+    ClipHorizontal:=flfClipHorizontal in Node.Flags;
+    ClipVertical:=flfClipVertical in Node.Flags;
+    if ClipHorizontal or ClipVertical then
+    begin
+      r:=El.GetRenderedPaddingBox;
+      //writeln('TFresnelElement.GetElementAts ',El.Name,' ClipH=',ClipHorizontal,' ClipV=',ClipVertical,' Hit=',FloatToCSSStr(HitX),',',FloatToCSSStr(HitY),' PadBox=',r.ToString);
+      if ClipHorizontal then
+      begin
+        if HitX<r.Left then exit;
+        if HitX>r.Right then exit;
+      end;
+      if ClipVertical then
+      begin
+        if HitY<r.Top then exit;
+        if HitY>r.Bottom then exit;
+      end;
+      aBar:=El.ScrollBarHorizontal;
+      //if aBar<>nil then writeln('Check ',aBar.Box.ToString);
+      if (aBar<>nil) and aBar.Box.Contains(HitX,HitY) then
+      begin
+        Arr:=[El];
+        Hit:=THit.hScrollbarHorz;
+        exit(true);
+      end;
+      aBar:=El.ScrollBarVertical;
+      if (aBar<>nil) and aBar.Box.Contains(HitX,HitY) then
+      begin
+        Arr:=[El];
+        Hit:=THit.hScrollbarVert;
+        exit(true);
+      end;
+    end;
+
     if Node.NodeCount>0 then begin
     if Node.NodeCount>0 then begin
       aContentOffset:=El.RenderedContentBox.TopLeft;
       aContentOffset:=El.RenderedContentBox.TopLeft;
       for i:=Node.NodeCount-1 downto 0 do
       for i:=Node.NodeCount-1 downto 0 do
-        Check(Node.Nodes[i],dx+aContentOffset.X,dy+aContentOffset.Y);
+      begin
+        if Check(Node.Nodes[i],dx+aContentOffset.X,dy+aContentOffset.Y)
+            and OnlyLast then
+          exit;
+      end;
     end;
     end;
     BorderBox:=El.RenderedBorderBox;
     BorderBox:=El.RenderedBorderBox;
-    if BorderBox.Contains(x-dx,y-dy) then
+    if BorderBox.Contains(HitX,HitY) then
     begin
     begin
-      System.Insert(El,Result,length(Result));
+      if length(Arr)=0 then
+      begin
+        Arr:=[El];
+        Hit:=THit.hElement;
+      end else begin
+        System.Insert(El,Arr,length(Arr));
+      end;
+      Result:=true;
     end;
     end;
   end;
   end;
 
 
 begin
 begin
-  Result:=[];
+  Arr:=[];
+  Hit:=THit.hNone;
   Check(LayoutNode,0,0);
   Check(LayoutNode,0,0);
+  //writeln('TFresnelViewport.GetElementsAt END ',Hit);
 end;
 end;
 
 
 function TFresnelViewport.ContentToPagePos(El: TFresnelElement; const x,
 function TFresnelViewport.ContentToPagePos(El: TFresnelElement; const x,
@@ -7136,20 +7200,19 @@ var
   NewPageXY, OldPageXY: TFresnelPoint;
   NewPageXY, OldPageXY: TFresnelPoint;
   i: Integer;
   i: Integer;
   j: SizeInt;
   j: SizeInt;
+  Hit: THit;
 begin
 begin
   if not (MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove,evtMouseEnter,evtMouseLeave]) then
   if not (MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove,evtMouseEnter,evtMouseLeave]) then
     exit;
     exit;
 
 
   NewPageXY:=WSData.PagePos;
   NewPageXY:=WSData.PagePos;
-  if evtMouseDown=MouseEventID then
-    Writeln('Ah');
-  NewHoverElements:=GetElementsAt(NewPageXY.X,NewPageXY.Y);
+  GetElementsAt(NewPageXY.X,NewPageXY.Y,false,NewHoverElements,Hit);
   OldHoverElements:=VPApplication.GetHoverElements;
   OldHoverElements:=VPApplication.GetHoverElements;
   OldMouseDownEl:=VPApplication.GetMouseDownElement(OldPageXY);
   OldMouseDownEl:=VPApplication.GetMouseDownElement(OldPageXY);
 
 
   MouseEvt:=nil;
   MouseEvt:=nil;
   try
   try
-    // send mouse leave events in stacking order top to bottom
+    // send mouse leave events in stacking order top to bottom aka child to parents
     for i:=0 to length(OldHoverElements)-1 do
     for i:=0 to length(OldHoverElements)-1 do
     begin
     begin
       El:=OldHoverElements[i];
       El:=OldHoverElements[i];
@@ -7174,9 +7237,9 @@ begin
       {$IFDEF VerboseFresnelMouse}
       {$IFDEF VerboseFresnelMouse}
       writeln('TFresnelViewport.WSMouseXY LEAVE VIEWPORT');
       writeln('TFresnelViewport.WSMouseXY LEAVE VIEWPORT');
       {$ENDIF}
       {$ENDIF}
-      FreeAndNil(MouseEvt);
       MouseEvt:=EventDispatcher.CreateEvent(Self,evtMouseLeave) as TFresnelMouseLeaveEvent;
       MouseEvt:=EventDispatcher.CreateEvent(Self,evtMouseLeave) as TFresnelMouseLeaveEvent;
       DispatchEvent(MouseEvt);
       DispatchEvent(MouseEvt);
+      FreeAndNil(MouseEvt);
     end;
     end;
 
 
     VPApplication.SetHoverElements(NewHoverElements);
     VPApplication.SetHoverElements(NewHoverElements);
@@ -7187,12 +7250,12 @@ begin
       {$IFDEF VerboseFresnelMouse}
       {$IFDEF VerboseFresnelMouse}
       writeln('TFresnelViewport.WSMouseXY ENTER VIEWPORT');
       writeln('TFresnelViewport.WSMouseXY ENTER VIEWPORT');
       {$ENDIF}
       {$ENDIF}
-      FreeAndNil(MouseEvt);
       MouseEvt:=EventDispatcher.CreateEvent(Self,evtMouseEnter) as TFresnelMouseEnterEvent;
       MouseEvt:=EventDispatcher.CreateEvent(Self,evtMouseEnter) as TFresnelMouseEnterEvent;
       EventDispatcher.DispatchEvent(MouseEvt);
       EventDispatcher.DispatchEvent(MouseEvt);
+      FreeAndNil(MouseEvt);
     end;
     end;
 
 
-    // sent mouse enter events in stacking order bottom to top
+    // sent mouse enter events in stacking order bottom to top aka parents to child
     for i:=length(NewHoverElements)-1 downto 0 do
     for i:=length(NewHoverElements)-1 downto 0 do
     begin
     begin
       El:=NewHoverElements[i];
       El:=NewHoverElements[i];
@@ -7203,9 +7266,9 @@ begin
       {$IFDEF VerboseFresnelMouse}
       {$IFDEF VerboseFresnelMouse}
       writeln('TFresnelViewport.WSMouseXY ENTER ',El.GetPath);
       writeln('TFresnelViewport.WSMouseXY ENTER ',El.GetPath);
       {$ENDIF}
       {$ENDIF}
-      FreeAndNil(MouseEvt);
       MouseEvt:=El.EventDispatcher.CreateEvent(El,evtMouseEnter) as TFresnelMouseEnterEvent;
       MouseEvt:=El.EventDispatcher.CreateEvent(El,evtMouseEnter) as TFresnelMouseEnterEvent;
       El.DispatchEvent(MouseEvt);
       El.DispatchEvent(MouseEvt);
+      FreeAndNil(MouseEvt);
     end;
     end;
 
 
   finally
   finally
@@ -7230,60 +7293,66 @@ begin
 
 
   if MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove] then
   if MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove] then
   begin
   begin
-    MouseEvt:=nil;
-    ClickEvt:=nil;
-    try
-      // dispatch mouse down/move/up events, in stacking order
-      i:=0;
-      MouseEvt:=El.EventDispatcher.CreateEvent(El,MouseEventId) as TFresnelMouseEvent;
-      MouseEvt.InitEvent(WSData);
-
-      BubbleEl:=El;
-      repeat
-        if MouseEvt is TBubbleMouseDownEvent then
-          TBubbleMouseDownEvent(MouseEvt).FElement:=BubbleEl
-        else if MouseEvt is TBubbleMouseUpEvent then
-          TBubbleMouseUpEvent(MouseEvt).FElement:=BubbleEl
-        else if MouseEvt is TBubbleMouseMoveEvent then
-          TBubbleMouseMoveEvent(MouseEvt).FElement:=BubbleEl;
-
-        {$IFDEF VerboseFresnelMouse}
-        writeln('TFresnelViewport.WSMouseXY ',MouseEventId,' ',BubbleEl.GetPath,' ',FloatToCSSStr(MouseEvt.X),',',FloatToCSSStr(MouseEvt.Y));
-        {$ENDIF}
-        BubbleEl.DispatchEvent(MouseEvt);
-        inc(i);
-        if (MouseEvt.PropagationStopped) or (i>=length(NewHoverElements)) then
-          break;
-        BubbleEl:=NewHoverElements[i];
-        if MouseEvt is TBubbleMouseDownEvent then
-          TBubbleMouseDownEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
-        else if MouseEvt is TBubbleMouseUpEvent then
-          TBubbleMouseUpEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
-        else if MouseEvt is TBubbleMouseMoveEvent then
-          TBubbleMouseMoveEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
-        else
-          break;
-      until false;
+    case Hit of
+    THit.hScrollbarHorz: El.ScrollBarHorizontal.MouseHandler(WSData,MouseEventId);
+    THit.hScrollbarVert: El.ScrollBarVertical.MouseHandler(WSData,MouseEventId);
+    else
+      MouseEvt:=nil;
+      ClickEvt:=nil;
+      try
+        // Dispatch mouse down/move/up events, in stacking order top to bottom aka child to parent,
+        // until an element stops the dispatch.
+        i:=0;
+        MouseEvt:=El.EventDispatcher.CreateEvent(El,MouseEventId) as TFresnelMouseEvent;
+        MouseEvt.InitEvent(WSData);
+
+        BubbleEl:=El;
+        repeat
+          if MouseEvt is TBubbleMouseDownEvent then
+            TBubbleMouseDownEvent(MouseEvt).FElement:=BubbleEl
+          else if MouseEvt is TBubbleMouseUpEvent then
+            TBubbleMouseUpEvent(MouseEvt).FElement:=BubbleEl
+          else if MouseEvt is TBubbleMouseMoveEvent then
+            TBubbleMouseMoveEvent(MouseEvt).FElement:=BubbleEl;
+
+          {$IFDEF VerboseFresnelMouse}
+          writeln('TFresnelViewport.WSMouseXY ',MouseEventId,' ',BubbleEl.GetPath,' ',FloatToCSSStr(MouseEvt.X),',',FloatToCSSStr(MouseEvt.Y));
+          {$ENDIF}
+          BubbleEl.DispatchEvent(MouseEvt);
+          inc(i);
+          if (MouseEvt.PropagationStopped) or (i>=length(NewHoverElements)) then
+            break;
+          BubbleEl:=NewHoverElements[i];
+          if MouseEvt is TBubbleMouseDownEvent then
+            TBubbleMouseDownEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
+          else if MouseEvt is TBubbleMouseUpEvent then
+            TBubbleMouseUpEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
+          else if MouseEvt is TBubbleMouseMoveEvent then
+            TBubbleMouseMoveEvent(MouseEvt).SetXY(PageToContentPos(BubbleEl,NewPageXY))
+          else
+            break;
+        until false;
 
 
-      FreeAndNil(MouseEvt);
+        FreeAndNil(MouseEvt);
 
 
 
 
-      if (MouseEventId=evtMouseUp) and (VPApplication.GetMouseDownElement(OldPageXY)=El) then
-      begin
-        // dispatch click event up the element hierarchy
-        ClickEvt:=El.EventDispatcher.CreateEvent(El,evtClick) as TFresnelMouseClickEvent;
-        Bubble(El,ClickEvt);
-        VPApplication.SetMouseDownElement(nil,NewPageXY);
-      end;
+        if (MouseEventId=evtMouseUp) and (VPApplication.GetMouseDownElement(OldPageXY)=El) then
+        begin
+          // dispatch click event up the element hierarchy
+          ClickEvt:=El.EventDispatcher.CreateEvent(El,evtClick) as TFresnelMouseClickEvent;
+          Bubble(El,ClickEvt);
+          VPApplication.SetMouseDownElement(nil,NewPageXY);
+        end;
 
 
-      // Focus gets set on mouse down. TrySetFocusControl will test whether the element actually can get focus.
-      // Todo: Test what if a non-focus handling element is positioned over one which does handle focus ?
-      // -> go over hover list ?
-      if (MouseEventId=evtMouseDown) and Assigned(El) then
-        TrySetFocusControl(El);
-    finally
-      MouseEvt.Free;
-      ClickEvt.Free;
+        // Focus gets set on mouse down. TrySetFocusControl will test whether the element actually can get focus.
+        // Todo: Test what if a non-focus handling element is positioned over one which does handle focus ?
+        // -> go over hover list ?
+        if (MouseEventId=evtMouseDown) and Assigned(El) then
+          TrySetFocusControl(El);
+      finally
+        MouseEvt.Free;
+        ClickEvt.Free;
+      end;
     end;
     end;
   end;
   end;
 end;
 end;
@@ -7296,9 +7365,8 @@ begin
     lEvt.Sender:=lElement;
     lEvt.Sender:=lElement;
     lElement.DispatchEvent(lEvt);
     lElement.DispatchEvent(lEvt);
     if lEvt.PropagationStopped then
     if lEvt.PropagationStopped then
-      lElement:=Nil
-    else
-      lElement:=lElement.Parent;
+      exit;
+    lElement:=lElement.Parent;
     end;
     end;
 end;
 end;
 
 
@@ -7316,7 +7384,7 @@ begin
     lFocus:=Self;
     lFocus:=Self;
   lEvtClass:=EventDispatcher.Registry.FindEventClass(KeyEventID);
   lEvtClass:=EventDispatcher.Registry.FindEventClass(KeyEventID);
   if not lEvtClass.InheritsFrom(TFresnelKeyEvent) then
   if not lEvtClass.InheritsFrom(TFresnelKeyEvent) then
-     Raise EFresnel.CreateFmt('%s is not a descendent of class TFresnelKeyEvent',[lEvtClass.ClassName]);
+    Raise EFresnel.CreateFmt('%s is not a descendent of class TFresnelKeyEvent',[lEvtClass.ClassName]);
   lEvt:=lKeyEvtClass.Create(WSData);
   lEvt:=lKeyEvtClass.Create(WSData);
   try
   try
     Bubble(lFocus,lEvt);
     Bubble(lFocus,lEvt);
@@ -7331,7 +7399,6 @@ var
   lFocus : TFresnelElement;
   lFocus : TFresnelElement;
   lEvt : TFresnelInputEvent;
   lEvt : TFresnelInputEvent;
 
 
-
 begin
 begin
   lFocus:=FFocusedElement;
   lFocus:=FFocusedElement;
   if not Assigned(lFocus) then
   if not Assigned(lFocus) then
@@ -7540,12 +7607,14 @@ procedure TFresnelElement.SetScrollLeft(const AValue: TFresnelLength);
 begin
 begin
   if FScrollLeft=AValue then Exit;
   if FScrollLeft=AValue then Exit;
   FScrollLeft:=AValue;
   FScrollLeft:=AValue;
+  DomChanged;
 end;
 end;
 
 
 procedure TFresnelElement.SetScrollTop(const AValue: TFresnelLength);
 procedure TFresnelElement.SetScrollTop(const AValue: TFresnelLength);
 begin
 begin
   if FScrollTop=AValue then Exit;
   if FScrollTop=AValue then Exit;
   FScrollTop:=AValue;
   FScrollTop:=AValue;
+  DomChanged;
 end;
 end;
 
 
 procedure TFresnelElement.SetViewportConnected(AValue: boolean);
 procedure TFresnelElement.SetViewportConnected(AValue: boolean);

+ 1 - 0
src/base/fresnel.edit.pp

@@ -407,6 +407,7 @@ end;
 procedure TEdit.HandleKeyUp(aEvent: TFresnelKeyEvent);
 procedure TEdit.HandleKeyUp(aEvent: TFresnelKeyEvent);
 begin
 begin
   // Do nothing for the moment.
   // Do nothing for the moment.
+  if aEvent=nil then ;
 end;
 end;
 
 
 procedure TEdit.HandleMouseMove(aEvent : TFresnelMouseEvent);
 procedure TEdit.HandleMouseMove(aEvent : TFresnelMouseEvent);

+ 217 - 121
src/base/fresnel.renderer.pas

@@ -21,14 +21,28 @@ unit Fresnel.Renderer;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Math, FPImage,
-//  LazLoggerBase,
-  Fresnel.Classes, Fresnel.DOM, Fresnel.Controls, Fresnel.Layouter;
+  Classes, SysUtils, Math, FPImage, FCL.Events,
+  {$IF FPC_FULLVERSION>30300}
+  System.UITypes,
+  {$ENDIF}
+  Fresnel.Classes, Fresnel.DOM, Fresnel.Controls, Fresnel.Layouter, Fresnel.Events;
 
 
 const
 const
-  DoublePi = 2*Pi;
+  Tau = 2*Pi;
 
 
 type
 type
+  TFresnelRenderer = class;
+
+  { TRendererScrollBar - simple custom drawn scrollbar with a round thumb bar - no buttons }
+
+  TRendererScrollBar = class(TFresnelScrollBar)
+  public
+    Border: TFresnelLength;
+    procedure Draw(Renderer: TFresnelRenderer); virtual;
+    procedure DrawCorner(Renderer: TFresnelRenderer; const r: TFresnelRect); virtual;
+    function GetHit(const X, Y: TFresnelLength; out aPosition: TFresnelLength): THit; override;
+    procedure MouseHandler(WSData: TFresnelMouseEventInit; MouseEventId: TEventID); override;
+  end;
 
 
   { TFresnelRenderer }
   { TFresnelRenderer }
 
 
@@ -73,10 +87,8 @@ type
     procedure DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground); virtual;
     procedure DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground); virtual;
     // Draw the border of the element. This is called after drawing the background. Not called if PrepareBackgroundBorder returned False.
     // Draw the border of the element. This is called after drawing the background. Not called if PrepareBackgroundBorder returned False.
     procedure DrawElBorder(El: TFresnelElement; Params: TBorderAndBackground); virtual;
     procedure DrawElBorder(El: TFresnelElement; Params: TBorderAndBackground); virtual;
-    function CreateScrollBar(El: TFresnelElement; Horizontal: boolean): TFresnelScrollBar; virtual;
+    function CreateScrollBar(El: TFresnelElement; Horizontal: boolean): TRendererScrollBar; virtual;
     // Draw the scrollbars of the element. Called after drawing content.
     // Draw the scrollbars of the element. Called after drawing content.
-    procedure DrawScrollBar(aBar: TFresnelScrollBar); virtual;
-    procedure DrawScrollBarCorner(El: TFresnelElement; const r: TFresnelRect); virtual;
     procedure DrawScrollBars(El: TFresnelElement); virtual;
     procedure DrawScrollBars(El: TFresnelElement); virtual;
     // Draw an element
     // Draw an element
     procedure DrawElement(El: TFresnelElement); virtual;
     procedure DrawElement(El: TFresnelElement); virtual;
@@ -107,7 +119,7 @@ type
     // Add 1 Shadow text to
     // Add 1 Shadow text to
     procedure AddTextShadow(const aX, aY: TFresnelLength; const aColor: TFPColor; const aRadius: TFresnelLength);
     procedure AddTextShadow(const aX, aY: TFresnelLength; const aColor: TFPColor; const aRadius: TFresnelLength);
     // Draw an elliptic arc with with given center and radii, from start to stop angle in rad (0=right), using specified color.
     // Draw an elliptic arc with with given center and radii, from start to stop angle in rad (0=right), using specified color.
-    procedure Arc(const aColor : TFPColor; const aCenter, aRadii : TFresnelPoint; aStartAngle : TFresnelLength = 0; aStopAngle : TFresnelLength = DoublePi); virtual; abstract;
+    procedure Arc(const aColor : TFPColor; const aCenter, aRadii : TFresnelPoint; aStartAngle : TFresnelLength = 0; aStopAngle : TFresnelLength = Tau); virtual; abstract;
     // Clear all text shadows
     // Clear all text shadows
     procedure ClearTextShadows;
     procedure ClearTextShadows;
     procedure ClipRect(const aRect: TFresnelRect); virtual; abstract;
     procedure ClipRect(const aRect: TFresnelRect); virtual; abstract;
@@ -138,6 +150,7 @@ type
   end;
   end;
   TFresnelRendererClass = class of TFresnelRenderer;
   TFresnelRendererClass = class of TFresnelRenderer;
 
 
+
 function DiameterToArc90DegCount(Diameter: single): integer; overload; // number of points to draw a 90deg arc
 function DiameterToArc90DegCount(Diameter: single): integer; overload; // number of points to draw a 90deg arc
 function RoundCorner2Polygon(const aRect: TFresnelRect; Corner: TFresnelCSSCorner): TFresnelPointArray; overload;
 function RoundCorner2Polygon(const aRect: TFresnelRect; Corner: TFresnelCSSCorner): TFresnelPointArray; overload;
 function RoundRect2Polygon(aRect: TFresnelRoundRect): TFresnelPointArray; overload;
 function RoundRect2Polygon(aRect: TFresnelRoundRect): TFresnelPointArray; overload;
@@ -374,6 +387,167 @@ begin
   b:=h;
   b:=h;
 end;
 end;
 
 
+{ TRendererScrollBar }
+
+procedure TRendererScrollBar.Draw(Renderer: TFresnelRenderer);
+var
+  TrackColor, ThumbColor: TFPColor;
+  FromPos, ToPos, l, Radius: TFresnelLength;
+  r: TFresnelRect;
+  ThumbR: TFresnelRoundRect;
+  c: TFresnelCSSCorner;
+  IsHovered: Boolean;
+begin
+  r:=Box;
+  IsHovered:=Element.IsHovered;
+
+  // draw track
+  TrackColor:=colLtGray;
+  if not IsHovered then
+    TrackColor.Alpha:=$8000;
+  Renderer.FillRect(TrackColor,r);
+  if Size<1 then
+    exit;
+
+  // draw thumb
+  ThumbColor:=colDkGray;
+  if not IsHovered then
+    ThumbColor.Alpha:=$8000;
+  FromPos:=Position/Size;
+  if FromPos<0 then FromPos:=0;
+  if FromPos>1 then FromPos:=1;
+  ToPos:=(Position+Page)/Size;
+  if ToPos<0 then ToPos:=0;
+  if ToPos>1 then ToPos:=1;
+  if Horizontal then
+  begin
+    if r.Width<r.Height then exit;
+    Border:=round(r.Height/6);
+  end else begin
+    if r.Height<r.Width then exit;
+    Border:=round(r.Width/6);
+  end;
+  Radius:=Border*2;
+  for c in TFresnelCSSCorner do begin
+    ThumbR.Radii[c].X:=Radius;
+    ThumbR.Radii[c].Y:=Radius;
+  end;
+  ThumbR.Box.Left:=r.Left+Border;
+  ThumbR.Box.Top:=r.Top+Border;
+  ThumbR.Box.Right:=r.Right-Border;
+  ThumbR.Box.Bottom:=r.Bottom-Border;
+  if Horizontal then
+  begin
+    l:=ThumbR.Box.Width;
+    ThumbR.Box.Left:=ThumbR.Box.Left+FromPos*l;
+    ThumbR.Box.Right:=ThumbR.Box.Left+Max((ToPos-FromPos)*l,2*Radius);
+  end else begin
+    l:=ThumbR.Box.Height;
+    ThumbR.Box.Top:=ThumbR.Box.Top+FromPos*l;
+    ThumbR.Box.Bottom:=ThumbR.Box.Top+Max((ToPos-FromPOs)*l,2*Radius);
+  end;
+
+  Renderer.RoundRect(ThumbColor,ThumbR,true);
+end;
+
+procedure TRendererScrollBar.DrawCorner(Renderer: TFresnelRenderer; const r: TFresnelRect);
+var
+  TrackColor: TFPColor;
+begin
+  TrackColor:=colLtGray;
+  if not Element.IsHovered then
+    TrackColor.Alpha:=$8000;
+  Renderer.FillRect(TrackColor,r);
+end;
+
+function TRendererScrollBar.GetHit(const X, Y: TFresnelLength; out aPosition: TFresnelLength): THit;
+var
+  LT, RB: TFresnelLength;
+begin
+  Result:=THit.hNone;
+  aPosition:=0;
+  if Horizontal then
+  begin
+    if (X<Box.Left) or (X>Box.Right) then
+      exit;
+    LT:=Box.Left+Border;
+    RB:=Box.Right-Border;
+    if LT>RB-1 then exit;
+    aPosition:=(X-LT) / (RB-LT);
+  end else begin
+    if (Y<Box.Top) or (Y>Box.Bottom) then
+      exit;
+    LT:=Box.Top+Border;
+    RB:=Box.Bottom-Border;
+    if LT>RB-1 then exit;
+    aPosition:=(Y-LT) / (RB-LT);
+  end;
+  if aPosition<0 then aPosition:=0;
+  if aPosition>1 then aPosition:=1;
+  aPosition:=aPosition*Size;
+
+  if aPosition<Position then
+    Result:=THit.hTrackLT
+  else if aPosition>=Position+Page then
+    Result:=THit.hTrackRB
+  else
+    Result:=THit.hThumb;
+end;
+
+procedure TRendererScrollBar.MouseHandler(WSData: TFresnelMouseEventInit; MouseEventId: TEventID);
+var
+  X, Y, NewPos: TFresnelLength;
+begin
+  X:=WSData.ControlPos.X+Element.RenderedContentBox.Left;
+  Y:=WSData.ControlPos.Y+Element.RenderedContentBox.Top;
+  {$IFDEF VerboseFresnelScrolling}
+  //writeln('TRendererScrollBar.MouseHandler ',Element.Name,' ControlPos=',WSData.ControlPos.ToString,', RenderBox=',Element.RenderedContentBox.ToString,' X=',FloatToCSSStr(X),',',FloatToCSSStr(Y),' Size=',FloatToCSSStr(Size),' Position=',FloatToCSSStr(Position),' Page=',FloatToCSSStr(Page));
+  {$ENDIF}
+
+  case MouseEventId of
+  evtMouseDown:
+    if WSData.Button=mbLeft then
+    begin
+      MouseDownHit:=GetHit(X,Y,MouseDownPos);
+      {$IFDEF VerboseFresnelScrolling}
+      writeln('TRendererScrollBar.MouseHandler Mouse Down ',Element.Name,' Hit=',MouseDownHit,' p=',FloatToCSSStr(MouseDownPos));
+      {$ENDIF}
+      case MouseDownHit of
+      THit.hButtonLT: ;
+      THit.hButtonRB: ;
+      THit.hThumb: exit;
+      THit.hTrackLT:
+        // scroll one page left/top
+        NewPos:=Position-Page;
+      THit.hTrackRB:
+        // scroll one page right/bottom
+        NewPos:=Position+Page;
+      else exit;
+      end;
+      NewPos:=Max(0,Min(NewPos,Size-Page));
+      if Horizontal then
+      begin
+        if Element.ComputedDirection=CSSRegistry.kwRTL then
+          NewPos:=NewPos-Size+Page;
+        Element.ScrollLeft:=NewPos;
+      end
+      else
+        Element.ScrollTop:=NewPos;
+    end;
+  evtMouseMove:
+    if MouseDownHit>THit.hNone then begin
+      //Hit:=GetHit(X,Y,p);
+      //writeln('TRendererScrollBar.MouseHandler Mouse Move ',Element.Name,' Hit=',Hit,' p=',FloatToCSSStr(p));
+    end;
+  evtMouseUp:
+    if (WSData.Button=mbLeft) and (MouseDownHit>THit.hNone) then
+    begin
+      //Hit:=GetHit(X,Y,p);
+      //writeln('TRendererScrollBar.MouseHandler Mouse Up ',Element.Name,' Hit=',Hit,' p=',FloatToCSSStr(p));
+    end;
+  end;
+end;
+
 { TFresnelRenderer }
 { TFresnelRenderer }
 
 
 procedure TFresnelRenderer.SetOrigin(const AValue: TFresnelPoint);
 procedure TFresnelRenderer.SetOrigin(const AValue: TFresnelPoint);
@@ -499,82 +673,9 @@ begin
 end;
 end;
 
 
 function TFresnelRenderer.CreateScrollBar(El: TFresnelElement; Horizontal: boolean
 function TFresnelRenderer.CreateScrollBar(El: TFresnelElement; Horizontal: boolean
-  ): TFresnelScrollBar;
-begin
-  Result:=TFresnelScrollBar.Create(El,Horizontal);
-end;
-
-procedure TFresnelRenderer.DrawScrollBar(aBar: TFresnelScrollBar);
-var
-  El: TFresnelElement;
-  TrackColor, ThumbColor: TFPColor;
-  FromPos, ToPos, l: TFresnelLength;
-  r: TFresnelRect;
-  Border, Radius: integer;
-  ThumbR: TFresnelRoundRect;
-  c: TFresnelCSSCorner;
-begin
-  El:=aBar.Element;
-  r:=aBar.Box;
-
-  // draw track
-  TrackColor:=colLtGray;
-  if not El.IsHovered then
-    TrackColor.Alpha:=$8000;
-  FillRect(TrackColor,r);
-  if aBar.Size<1 then
-    exit;
-
-  // draw thumb
-  ThumbColor:=colDkGray;
-  if not El.IsHovered then
-    ThumbColor.Alpha:=$8000;
-  FromPos:=aBar.Position/aBar.Size;
-  if FromPos<0 then FromPos:=0;
-  if FromPos>1 then FromPos:=1;
-  ToPos:=(aBar.Position+aBar.Page)/aBar.Size;
-  if ToPos<0 then ToPos:=0;
-  if ToPos>1 then ToPos:=1;
-  if aBar.Horizontal then
-  begin
-    if r.Width<r.Height then exit;
-    Border:=round(r.Height/6);
-  end else begin
-    if r.Height<r.Width then exit;
-    Border:=round(r.Width/6);
-  end;
-  Radius:=Border*2;
-  for c in TFresnelCSSCorner do begin
-    ThumbR.Radii[c].X:=Radius;
-    ThumbR.Radii[c].Y:=Radius;
-  end;
-  ThumbR.Box.Left:=r.Left+Border;
-  ThumbR.Box.Top:=r.Top+Border;
-  ThumbR.Box.Right:=r.Right-Border;
-  ThumbR.Box.Bottom:=r.Bottom-Border;
-  if aBar.Horizontal then
-  begin
-    l:=ThumbR.Box.Width-2*Radius;
-    ThumbR.Box.Left:=ThumbR.Box.Left+FromPos*l;
-    ThumbR.Box.Right:=ThumbR.Box.Left+2*Radius+ToPos*l;
-  end else begin
-    l:=ThumbR.Box.Height-2*Radius;
-    ThumbR.Box.Top:=ThumbR.Box.Top+FromPos*l;
-    ThumbR.Box.Bottom:=ThumbR.Box.Top+2*Radius+ToPos*l;
-  end;
-
-  RoundRect(ThumbColor,ThumbR,true);
-end;
-
-procedure TFresnelRenderer.DrawScrollBarCorner(El: TFresnelElement; const r: TFresnelRect);
-var
-  TrackColor: TFPColor;
+  ): TRendererScrollBar;
 begin
 begin
-  if El=nil then ;
-  TrackColor:=colLtGray;
-  if not El.IsHovered then
-    TrackColor.Alpha:=$8000;
-  FillRect(TrackColor,r);
+  Result:=TRendererScrollBar.Create(El,Horizontal);
 end;
 end;
 
 
 procedure TFresnelRenderer.DrawScrollBars(El: TFresnelElement);
 procedure TFresnelRenderer.DrawScrollBars(El: TFresnelElement);
@@ -582,7 +683,7 @@ var
   LNode: TUsedLayoutNode;
   LNode: TUsedLayoutNode;
   BarWidth, BarHeight: TFresnelLength;
   BarWidth, BarHeight: TFresnelLength;
   r, aPaddingBox: TFresnelRect;
   r, aPaddingBox: TFresnelRect;
-  aScrollbar: TFresnelScrollBar;
+  aScrollbar: TRendererScrollBar;
   IsLeft, HasVertBar, HasHorzBar: Boolean;
   IsLeft, HasVertBar, HasHorzBar: Boolean;
 begin
 begin
   LNode:=TUsedLayoutNode(El.LayoutNode);
   LNode:=TUsedLayoutNode(El.LayoutNode);
@@ -607,7 +708,7 @@ begin
   aPaddingBox:=El.GetRenderedPaddingBox;
   aPaddingBox:=El.GetRenderedPaddingBox;
   //writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' PaddingBox=',aPaddingBox.ToString);
   //writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' PaddingBox=',aPaddingBox.ToString);
 
 
-  aScrollbar:=El.ScrollBarVertical;
+  aScrollbar:=El.ScrollBarVertical as TRendererScrollBar;
   if HasVertBar then
   if HasVertBar then
   begin
   begin
     r:=aPaddingBox;
     r:=aPaddingBox;
@@ -623,25 +724,21 @@ begin
     else begin
     else begin
       if aScrollbar=nil then
       if aScrollbar=nil then
         aScrollbar:=CreateScrollBar(El,false);
         aScrollbar:=CreateScrollBar(El,false);
-      try
-        aScrollbar.Size:=LNode.ScrollHeight;
-        aScrollbar.Position:=El.ScrollTop;
-        aScrollbar.Page:=El.ClientHeight;
-        aScrollbar.Box:=r;
-        aScrollbar.Color:=El.GetComputedColor(fcaScrollbarColor,colGray);
-
-        {$IFDEF VerboseFresnelScrolling}
-        writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' ScrollHeight=',FloatToCSSStr(El.ScrollHeight),' ClientHeight',FloatToCSSStr(El.ClientHeight),' ScrollTop=',FloatToCSSStr(El.ScrollTop)+' BarWidth='+FloatToCSSStr(BarWidth)+' r='+r.ToString);
-        {$ENDIF}
-        DrawScrollBar(aScrollbar);
-      finally
-        aScrollbar.Free;
-      end;
+      aScrollbar.Size:=LNode.ScrollHeight;
+      aScrollbar.Page:=El.ClientHeight;
+      aScrollbar.Position:=El.ScrollTop;
+      aScrollbar.Box:=r;
+      aScrollbar.Color:=El.GetComputedColor(fcaScrollbarColor,colGray);
+
+      {$IFDEF VerboseFresnelScrolling}
+      writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' ScrollHeight=',FloatToCSSStr(El.ScrollHeight),' ClientHeight=',FloatToCSSStr(El.ClientHeight),' ScrollTop=',FloatToCSSStr(El.ScrollTop)+' BarWidth='+FloatToCSSStr(BarWidth)+' r='+r.ToString);
+      {$ENDIF}
+      aScrollbar.Draw(Self);
     end;
     end;
   end else if aScrollbar<>nil then
   end else if aScrollbar<>nil then
     aScrollbar.Free;
     aScrollbar.Free;
 
 
-  aScrollbar:=El.ScrollBarHorizontal;
+  aScrollbar:=El.ScrollBarHorizontal as TRendererScrollBar;
   if HasHorzBar then
   if HasHorzBar then
   begin
   begin
     r:=aPaddingBox;
     r:=aPaddingBox;
@@ -659,20 +756,19 @@ begin
     else begin
     else begin
       if aScrollbar=nil then
       if aScrollbar=nil then
         aScrollbar:=CreateScrollBar(El,true);
         aScrollbar:=CreateScrollBar(El,true);
-      try
-        aScrollbar.Size:=LNode.ScrollWidth;
+      aScrollbar.Size:=LNode.ScrollWidth;
+      aScrollbar.Page:=El.ClientWidth;
+      if El.ComputedDirection=CSSRegistry.kwRTL then
+        aScrollbar.Position:=El.ScrollLeft+aScrollbar.Size-aScrollbar.Page
+      else
         aScrollbar.Position:=El.ScrollLeft;
         aScrollbar.Position:=El.ScrollLeft;
-        aScrollbar.Page:=El.ClientWidth;
-        aScrollbar.Box:=r;
-        aScrollbar.Color:=El.GetComputedColor(fcaScrollbarColor,colGray);
-
-        {$IFDEF VerboseFresnelScrolling}
-        writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' ScrollWidth=',FloatToCSSStr(El.ScrollWidth),' ClientWidth=',FloatToCSSStr(El.ClientWidth),' ScrollLeft=',FloatToCSSStr(El.ScrollLeft)+' BarHeight='+FloatToCSSStr(BarHeight));
-        {$ENDIF}
-        DrawScrollBar(aScrollbar);
-      finally
-        aScrollbar.Free;
-      end;
+      aScrollbar.Box:=r;
+      aScrollbar.Color:=El.GetComputedColor(fcaScrollbarColor,colGray);
+
+      {$IFDEF VerboseFresnelScrolling}
+      writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' ScrollWidth=',FloatToCSSStr(El.ScrollWidth),' ClientWidth=',FloatToCSSStr(El.ClientWidth),' ScrollLeft=',FloatToCSSStr(El.ScrollLeft)+' BarHeight='+FloatToCSSStr(BarHeight));
+      {$ENDIF}
+      aScrollbar.Draw(Self);
     end;
     end;
   end else if aScrollbar<>nil then
   end else if aScrollbar<>nil then
     aScrollbar.Free;
     aScrollbar.Free;
@@ -690,7 +786,7 @@ begin
       r.Left:=Max(aPaddingBox.Left,r.Right-BarWidth);
       r.Left:=Max(aPaddingBox.Left,r.Right-BarWidth);
     end;
     end;
     if not r.IsEmpty then
     if not r.IsEmpty then
-      DrawScrollbarCorner(El,r);
+      aScrollbar.DrawCorner(Self,r);
   end;
   end;
 end;
 end;
 
 
@@ -703,7 +799,7 @@ var
   s: TFresnelCSSSide;
   s: TFresnelCSSSide;
   Corner: TFresnelCSSCorner;
   Corner: TFresnelCSSCorner;
   aClientWidth, aClientHeight: TFresnelLength;
   aClientWidth, aClientHeight: TFresnelLength;
-  NeedClipHorizontal, NeedClipVertical, NeedRestore: Boolean;
+  ClipHorizontal, ClipVertical, NeedRestore: Boolean;
 begin
 begin
   FLLog(etDebug,'TFresnelRenderer.DrawElement %s Origin=%s',[El.GetPath,Origin.ToString]);
   FLLog(etDebug,'TFresnelRenderer.DrawElement %s Origin=%s',[El.GetPath,Origin.ToString]);
   LNode:=TUsedLayoutNode(El.LayoutNode);
   LNode:=TUsedLayoutNode(El.LayoutNode);
@@ -761,17 +857,17 @@ begin
   end;
   end;
 
 
   NeedRestore:=false;
   NeedRestore:=false;
-  NeedClipHorizontal:=flfClipHorizontal in LNode.Flags;
-  NeedClipVertical:=flfClipVertical in LNode.Flags;
-  if NeedClipHorizontal or NeedClipVertical then
+  ClipHorizontal:=flfClipHorizontal in LNode.Flags;
+  ClipVertical:=flfClipVertical in LNode.Flags;
+  if ClipHorizontal or ClipVertical then
   begin
   begin
-    //writeln('TFresnelRenderer.DrawElement ',El.Name,' ',NeedClipHorizontal,' ',NeedClipVertical);
+    //writeln('TFresnelRenderer.DrawElement ',El.Name,' ',ClipHorizontal,' ',ClipVertical);
     r:=El.GetRenderedPaddingBox;
     r:=El.GetRenderedPaddingBox;
-    if not NeedClipHorizontal then
+    if not ClipHorizontal then
     begin
     begin
       r.Left:=-Origin.X;
       r.Left:=-Origin.X;
       r.Right:=El.Viewport.Width-Origin.X;
       r.Right:=El.Viewport.Width-Origin.X;
-    end else if not NeedClipVertical then
+    end else if not ClipVertical then
     begin
     begin
       r.Top:=-Origin.Y;
       r.Top:=-Origin.Y;
       r.Bottom:=El.Viewport.Height-Origin.Y;
       r.Bottom:=El.Viewport.Height-Origin.Y;

+ 1 - 1
src/lcl/fresnel.lcl.pas

@@ -89,7 +89,7 @@ type
   public
   public
     function RadToLCLAngle16(Angle: TFresnelLength): integer;
     function RadToLCLAngle16(Angle: TFresnelLength): integer;
     procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
     procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
-      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = DoublePi); override;
+      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = Tau); override;
     procedure ClipRect(const aRect: TFresnelRect); override;
     procedure ClipRect(const aRect: TFresnelRect); override;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
       const aImage: TFPCustomImage); override;
       const aImage: TFPCustomImage); override;

+ 1 - 1
src/skia/fresnel.skiarenderer.pas

@@ -149,7 +149,7 @@ type
       const aColor: TFPColor; const aRadius: TFresnelLength; const aTextBlob: ISkTextBlob); virtual;
       const aColor: TFPColor; const aRadius: TFresnelLength; const aTextBlob: ISkTextBlob); virtual;
  Public
  Public
     procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
     procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
-      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = DoublePi); override;
+      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = Tau); override;
     procedure ClipRect(const aRect: TFresnelRect); override;
     procedure ClipRect(const aRect: TFresnelRect); override;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
       const aImage: TFPCustomImage); override;
       const aImage: TFPCustomImage); override;