2
0
Эх сурвалжийг харах

scrolling: introduced UsedClientBox, removed RenderedContentBox

mattias 4 сар өмнө
parent
commit
1598753b02

+ 5 - 5
demo/Button/MainUnit.pas

@@ -43,27 +43,27 @@ end;
 
 procedure TMainForm.Div1MouseDown(Event: TFresnelMouseEvent);
 begin
-  writeln('TMainForm.Div1MouseDown ',Event.ControlX,',',Event.ControlY);
+  writeln('TMainForm.Div1MouseDown ',Event.ClientX,',',Event.ClientY);
 end;
 
 procedure TMainForm.Div1MouseMove(Event: TFresnelMouseEvent);
 begin
-  writeln('TMainForm.Div1MouseMove ',Event.ControlX,',',Event.ControlY);
+  writeln('TMainForm.Div1MouseMove ',Event.ClientX,',',Event.ClientY);
 end;
 
 procedure TMainForm.Label1MouseDown(Event: TFresnelMouseEvent);
 begin
-  writeln('TMainForm.Label1MouseDown ',Event.ControlX,',',Event.ControlY);
+  writeln('TMainForm.Label1MouseDown ',Event.ClientX,',',Event.ClientY);
 end;
 
 procedure TMainForm.Label1MouseMove(Event: TFresnelMouseEvent);
 begin
-  writeln('TMainForm.Label1MouseMove ',Event.ControlX,',',Event.ControlY);
+  writeln('TMainForm.Label1MouseMove ',Event.ClientX,',',Event.ClientY);
 end;
 
 procedure TMainForm.Label1MouseUp(Event: TFresnelMouseEvent);
 begin
-  writeln('TMainForm.Label1MouseUp ',Event.ControlX,',',Event.ControlY);
+  writeln('TMainForm.Label1MouseUp ',Event.ClientX,',',Event.ClientY);
 end;
 
 procedure TMainForm.MainFormCreate(Sender: TObject);

+ 3 - 3
demo/lcl/Button/FreBtnForm.pas

@@ -44,17 +44,17 @@ end;
 
 procedure TFresnelButtonForm.Label1MouseDown(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseDown ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseDown ',Event.ClientX,',',Event.ClientY]);
 end;
 
 procedure TFresnelButtonForm.Label1MouseMove(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseMove ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseMove ',Event.ClientX,',',Event.ClientY]);
 end;
 
 procedure TFresnelButtonForm.Label1MouseUp(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseUp ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseUp ',Event.ClientX,',',Event.ClientY]);
 end;
 
 end.

+ 1 - 1
demo/lclcontrol/DivAndSpan/mainunit.pas

@@ -230,7 +230,7 @@ begin
     LogEventData(Event);
   Btn:=GetEnumName(TypeInfo(TMouseButton),Ord(Event.Button)) ;
   Btns:=SetToString(PTypeInfo(TypeInfo(TMouseButtons)),Longint(EVent.Buttons),True);
-  DoLog('Mouse Event (X: %f, Y: %f, Button: %s, Buttons: %s) ', [Event.ControlX,Event.ControlY,Btn,Btns]);
+  DoLog('Mouse Event (X: %f, Y: %f, Button: %s, Buttons: %s) ', [Event.ClientX,Event.ClientY,Btn,Btns]);
 end;
 
 procedure TMainForm.DoLog(const Msg : String);

+ 3 - 2
src/base/fresnel.controls.pas

@@ -346,7 +346,8 @@ procedure TCustomImage.DoRender(aRenderer: IFresnelRenderer);
 begin
   inherited DoRender(aRenderer);
   if Assigned(FImage.Data) then
-    aRenderer.DrawImage(RenderedContentBox.Left,RenderedContentBox.Top,RenderedContentBox.Width,RenderedContentBox.Height,Fimage.Data);
+    aRenderer.DrawImage(UsedClientBox.Left,UsedClientBox.Top,UsedClientBox.Width,UsedClientBox.Height,
+                        FImage.Data);
 end;
 
 constructor TCustomImage.Create(AOwner: TComponent);
@@ -502,7 +503,7 @@ begin
   if HaveShadow then
     aRenderer.AddTextShadow(aOffsetX,aOffsetY,ShadowColor,aRadius);
 
-  aRenderer.TextOut(RenderedContentBox.Left,RenderedContentBox.Top,Font,aColorFP,aCaption);
+  aRenderer.TextOut(UsedClientBox.Left,UsedClientBox.Top,Font,aColorFP,aCaption);
   if HaveShadow then
     aRenderer.ClearTextShadows;
 end;

+ 246 - 159
src/base/fresnel.dom.pas

@@ -1309,8 +1309,6 @@ type
     FLayoutNode: TFresnelLayoutNode;
     FFontDesc: TFresnelFontDesc;
     FRendered: boolean;
-    FRenderedBorderBox: TFresnelRect;
-    FRenderedContentBox: TFresnelRect;
     FScrollBarHorizontal: TPseudoElScrollBar;
     FScrollBarVertical: TPseudoElScrollBar;
     FScrollLeft: TFresnelLength;
@@ -1320,6 +1318,7 @@ type
     FStandardEvents : Array[0..evtLastEvent] of TEventHandlerItem;
     FEventDispatcher : TFresnelEventDispatcher;
     FUsedBorderBox: TFresnelRect;
+    FUsedClientBox: TFresnelRect;
     FUsedContentBox: TFresnelRect;
     function GetClientHeight: TFresnelLength;
     function GetClientWidth: TFresnelLength;
@@ -1504,13 +1503,18 @@ type
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFresnelPoint; virtual; // ignoring min|max-height
     function GetContainerContentWidth(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
     function GetContainerContentHeight(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
-    function GetContainerOffset: TFresnelPoint; virtual;
+    function GetContainerOffset: TFresnelPoint;
     function IsBlockFormattingContext: boolean; virtual;
+    function GetUsedPaddingBox: TFresnelRect;
+    function GetBorderBoxOnViewport: TFresnelRect;
+    function ClientToBorderPos(const X, Y: TFresnelLength): TFresnelPoint;
+    function BorderToClientPos(const X, Y: TFresnelLength): TFresnelPoint;
     property ClientWidth: TFresnelLength read GetClientWidth;
     property ClientHeight: TFresnelLength read GetClientHeight;
     property LayoutNode: TFresnelLayoutNode read FLayoutNode write FLayoutNode;
-    property UsedBorderBox: TFresnelRect read FUsedBorderBox write FUsedBorderBox; // relative to layout parent's used contentbox
-    property UsedContentBox: TFresnelRect read FUsedContentBox write FUsedContentBox; // relative to layout parent's used contentbox
+    property UsedBorderBox: TFresnelRect read FUsedBorderBox write FUsedBorderBox; // relative to layout parent's clientbox
+    property UsedContentBox: TFresnelRect read FUsedContentBox write FUsedContentBox; // the content including scroll gutters, relative to layout parent's clientbox
+    property UsedClientBox: TFresnelRect read FUsedClientBox write FUsedClientBox; // the content excluding scroll gutters, relative to layout parent's clientbox
     // Scrolling
     property ScrollLeft: TFresnelLength read FScrollLeft write SetScrollLeft;
     property ScrollTop: TFresnelLength read FScrollTop write SetScrollTop;
@@ -1519,11 +1523,7 @@ type
     property ScrollBarHorizontal: TPseudoElScrollBar read FScrollBarHorizontal;
     property ScrollBarVertical: TPseudoElScrollBar read FScrollBarVertical;
     // Renderer
-    function GetBorderBoxOnViewport: TFresnelRect; virtual;
-    function GetRenderedPaddingBox: TFresnelRect;
     property Rendered: boolean read FRendered write FRendered;
-    property RenderedBorderBox: TFresnelRect read FRenderedBorderBox write FRenderedBorderBox; // relative to layout parent's rendered contentbox
-    property RenderedContentBox: TFresnelRect read FRenderedContentBox write FRenderedContentBox; // relative to layout parent's rendered contentbox
     // Events
     function AddEventListener(aID : TEventID; aHandler : TFresnelEventHandler) : Integer;
     function AddEventListener(Const aName: TEventName; aHandler : TFresnelEventHandler) : Integer;
@@ -1609,7 +1609,6 @@ type
     MouseDownPos: TFresnelLength;
     constructor Create(TheElement: TFresnelElement; aHorizontal: boolean); overload;
     function GetHit(const X, Y: TFresnelLength; out aPosition: TFresnelLength): THit; virtual; abstract;
-    procedure MouseHandler(WSData: TFresnelMouseEventInit; MouseEventId: TEventID); virtual; abstract;
     property Horizontal: boolean read FHorizontal;
   end;
 
@@ -1632,9 +1631,9 @@ type
 
   IFresnelVPApplication = interface ['{2075A1FE-F729-4083-8CE5-01932ADB4D69}']
     function GetHoverElements: TFresnelElementArray;
-    function GetMouseDownElement(out PageXY: TFresnelPoint): TFresnelElement;
+    function GetMouseDownElement(out ViewportXY: TFresnelPoint): TFresnelElement;
     procedure SetHoverElements(const ElArr: TFresnelElementArray);
-    procedure SetMouseDownElement(El: TFresnelElement; const PageXY: TFresnelPoint);
+    procedure SetMouseDownElement(El: TFresnelElement; const ViewportXY: TFresnelPoint);
   end;
 
   TFresnelVPPointerCapture = record
@@ -1776,15 +1775,19 @@ type
     class function CSSTypeID: TCSSNumericalID; override;
     class function CSSTypeName: TCSSString; override;
     class function GetCSSTypeStyle: TCSSString; override;
-    function GetElementAt(const x, y: TFresnelLength): TFresnelElement; virtual;
-    function GetElementsAt(const x, y: TFresnelLength): TFresnelElementArray; virtual;
+    function GetElementAt(const x, y: TFresnelLength): TFresnelElement;
+    function GetElementsAt(const x, y: TFresnelLength): TFresnelElementArray;
     // 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 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 p: TFresnelPoint): TFresnelPoint; virtual; overload; // content of viewport to content box of El
+      out Arr: TFresnelElementArray; out Hit: THit); virtual; // viewport coords
+    function ClientPosToViewport(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; overload; // child coord to viewport
+    function ViewportToClientPos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; overload; // viewport to child coord
+    function ViewportToClientPos(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; overload; // viewport to child coord
+    function PaddingBoxToViewport(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; overload; // padding-box coord of El to viewport
+    function PaddingBoxToViewport(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; overload; // padding-box of El to viewport
+    function ViewportToPaddingBox(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; overload; // viewport to padding-box coord of El
+    function ViewportToPaddingBox(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; overload; // viewport to padding-box coord of El
+    function GetPointerCaptureEl(const aPointerId: TFreHandle): TFresnelElement;
     procedure SetPointerCapture(const aPointerId: TFreHandle); override; overload;
     procedure SetPointerCapture(El: TFresnelElement; const aPointerId: TFreHandle); virtual; overload;
     procedure ReleasePointerCapture(const aPointerId: TFreHandle); override; overload;
@@ -6604,7 +6607,7 @@ end;
 
 constructor TPseudoElScrollBar.Create(TheElement: TFresnelElement; aHorizontal: boolean);
 begin
-  inherited Create(TheElement);
+  Create(TheElement);
   FHorizontal:=aHorizontal;
   Parent:=TheElement;
   if Horizontal then
@@ -7075,46 +7078,43 @@ procedure TFresnelViewport.GetElementsAt(const x, y: TFresnelLength; OnlyLast: b
   var
     El: TFresnelElement;
     i: Integer;
-    BorderBox, r: TFresnelRect;
+    BorderBox, r, c: TFresnelRect;
     aContentOffset: TFresnelPoint;
-    HitX, HitY: TFresnelLength;
     aBar: TPseudoElScrollBar;
     ClipHorizontal, ClipVertical: Boolean;
+    CurX, CurY: TFresnelLength;
   begin
     Result:=false;
     if Node=nil then exit;
     El:=Node.Element;
     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;
+      r:=El.GetUsedPaddingBox;
       //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;
+        if dx<r.Left then exit;
+        if dx>r.Right then exit;
       end;
       if ClipVertical then
       begin
-        if HitY<r.Top then exit;
-        if HitY>r.Bottom then exit;
+        if dy<r.Top then exit;
+        if dy>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
+      if (aBar<>nil) and aBar.Box.Contains(dx,dy) then
       begin
         Arr:=[El];
         Hit:=THit.hScrollbarHorz;
         exit(true);
       end;
       aBar:=El.ScrollBarVertical;
-      if (aBar<>nil) and aBar.Box.Contains(HitX,HitY) then
+      if (aBar<>nil) and aBar.Box.Contains(dx,dy) then
       begin
         Arr:=[El];
         Hit:=THit.hScrollbarVert;
@@ -7123,16 +7123,17 @@ procedure TFresnelViewport.GetElementsAt(const x, y: TFresnelLength; OnlyLast: b
     end;
 
     if Node.NodeCount>0 then begin
-      aContentOffset:=El.RenderedContentBox.TopLeft;
+      c:=El.UsedClientBox;
+      CurX:=dx-c.Left+El.ScrollLeft;
+      CurY:=dy-c.Top+El.ScrollTop;
       for i:=Node.NodeCount-1 downto 0 do
       begin
-        if Check(Node.Nodes[i],dx+aContentOffset.X,dy+aContentOffset.Y)
-            and OnlyLast then
+        if Check(Node.Nodes[i],CurX,CurY) and OnlyLast then
           exit;
       end;
     end;
-    BorderBox:=El.RenderedBorderBox;
-    if BorderBox.Contains(HitX,HitY) then
+    BorderBox:=El.UsedBorderBox;
+    if BorderBox.Contains(dx,dy) then
     begin
       if length(Arr)=0 then
       begin
@@ -7148,52 +7149,102 @@ procedure TFresnelViewport.GetElementsAt(const x, y: TFresnelLength; OnlyLast: b
 begin
   Arr:=[];
   Hit:=THit.hNone;
-  Check(LayoutNode,0,0);
+  Check(LayoutNode,x,y);
   //writeln('TFresnelViewport.GetElementsAt END ',Hit);
 end;
 
-function TFresnelViewport.ContentToPagePos(El: TFresnelElement; const x,
-  y: TFresnelLength): TFresnelPoint;
+function TFresnelViewport.ClientPosToViewport(El: TFresnelElement; const x, y: TFresnelLength
+  ): TFresnelPoint;
 var
   Node: TFresnelLayoutNode;
-  aContentOffset: TFresnelPoint;
+  c: TFresnelRect;
 begin
   Result.X:=x;
   Result.Y:=y;
-  if not El.Rendered then exit;
   Node:=El.LayoutNode;
-
+  if Node.SkipRendering then exit;
+  c:=El.UsedClientBox;
+  Result.X+=c.Left;
+  Result.Y+=c.Top;
   repeat
-    aContentOffset:=El.RenderedContentBox.TopLeft;
-    Result.X+=aContentOffset.X;
-    Result.Y+=aContentOffset.Y;
+    Node:=Node.Parent;
     if Node=nil then exit;
+    El:=Node.Element;
+    c:=El.UsedClientBox;
+    Result.X+=c.Left-El.ScrollLeft;
+    Result.Y+=c.Top-El.ScrollTop;
+  until false;
+end;
+
+function TFresnelViewport.ViewportToClientPos(El: TFresnelElement; const x, y: TFresnelLength
+  ): TFresnelPoint;
+var
+  p: TFresnelPoint;
+begin
+  p:=ClientPosToViewport(El,0,0);
+  Result.X:=x-p.x;
+  Result.Y:=y-p.y;
+end;
+
+function TFresnelViewport.ViewportToClientPos(El: TFresnelElement; const p: TFresnelPoint
+  ): TFresnelPoint;
+begin
+  Result:=ViewportToClientPos(El,p.X,p.Y);
+end;
+
+function TFresnelViewport.PaddingBoxToViewport(El: TFresnelElement; const x, y: TFresnelLength
+  ): TFresnelPoint;
+var
+  Node: TFresnelLayoutNode;
+  c: TFresnelRect;
+begin
+  Result.X:=x;
+  Result.Y:=y;
+  Node:=El.LayoutNode;
+  if Node.SkipRendering then exit;
+  Result.X+=Node.BorderLeft+El.UsedBorderBox.Left;
+  Result.Y+=Node.BorderTop+El.UsedBorderBox.Top;
+  repeat
     Node:=Node.Parent;
     if Node=nil then exit;
     El:=Node.Element;
-  until El=nil;
+    c:=El.UsedClientBox;
+    Result.X+=c.Left-El.ScrollLeft;
+    Result.Y+=c.Top-El.ScrollTop;
+  until false;
 end;
 
-function TFresnelViewport.ContentToPagePos(El: TFresnelElement; const p: TFresnelPoint
+function TFresnelViewport.PaddingBoxToViewport(El: TFresnelElement; const p: TFresnelPoint
   ): TFresnelPoint;
 begin
-  Result:=ContentToPagePos(El,p.X,p.Y);
+  Result:=PaddingBoxToViewport(El,p.X,p.Y);
 end;
 
-function TFresnelViewport.PageToContentPos(El: TFresnelElement; const x,
-  y: TFresnelLength): TFresnelPoint;
+function TFresnelViewport.ViewportToPaddingBox(El: TFresnelElement; const x, y: TFresnelLength
+  ): TFresnelPoint;
 var
   p: TFresnelPoint;
 begin
-  p:=ContentToPagePos(El,0,0);
+  p:=PaddingBoxToViewport(El,0,0);
   Result.X:=x-p.X;
   Result.Y:=y-p.Y;
 end;
 
-function TFresnelViewport.PageToContentPos(El: TFresnelElement; const p: TFresnelPoint
+function TFresnelViewport.ViewportToPaddingBox(El: TFresnelElement; const p: TFresnelPoint
   ): TFresnelPoint;
 begin
-  Result:=PageToContentPos(El,p.X,p.Y);
+  Result:=ViewportToPaddingBox(El,p.X,p.Y);
+end;
+
+function TFresnelViewport.GetPointerCaptureEl(const aPointerId: TFreHandle): TFresnelElement;
+var
+  i: Integer;
+begin
+  if aPointerId=nil then exit(nil);
+  for i:=0 to length(FPointerCaptures)-1 do
+    if FPointerCaptures[i].Id=aPointerId then
+      exit(FPointerCaptures[i].Element);
+  Result:=nil;
 end;
 
 procedure TFresnelViewport.SetPointerCapture(const aPointerId: TFreHandle);
@@ -7294,11 +7345,11 @@ end;
 
 procedure TFresnelViewport.WSMouseXY(WSData: TFresnelMouseEventInit; MouseEventId: TEventID);
 var
-  El, BubbleEl, OldMouseDownEl: TFresnelElement;
+  El, BubbleEl, OldMouseDownEl, CaptEl: TFresnelElement;
   MouseEvt: TFresnelMouseEvent;
   ClickEvt: TFresnelMouseClickEvent;
   NewHoverElements, OldHoverElements: TFresnelElementArray;
-  NewPageXY, OldPageXY: TFresnelPoint;
+  NewViewportXY, OldViewportXY, NewPageXY: TFresnelPoint;
   i: Integer;
   j: SizeInt;
   Hit: THit;
@@ -7306,10 +7357,16 @@ begin
   if not (MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove,evtMouseEnter,evtMouseLeave]) then
     exit;
 
-  NewPageXY:=WSData.PagePos;
-  GetElementsAt(NewPageXY.X,NewPageXY.Y,false,NewHoverElements,Hit);
+  // compute PagePos
+  NewViewportXY:=WSData.ViewportPos;
+  NewPageXY.X:=NewViewportXY.X-UsedClientBox.Left+ScrollLeft;
+  NewPageXY.Y:=NewViewportXY.Y-UsedClientBox.Top+ScrollTop;
+  WSData.PagePos:=NewPageXY;
+
+  // hover: get all elements under the pointer
+  GetElementsAt(NewViewportXY.X,NewViewportXY.Y,false,NewHoverElements,Hit);
   OldHoverElements:=VPApplication.GetHoverElements;
-  OldMouseDownEl:=VPApplication.GetMouseDownElement(OldPageXY);
+  OldMouseDownEl:=VPApplication.GetMouseDownElement(OldViewportXY);
 
   MouseEvt:=nil;
   try
@@ -7376,84 +7433,93 @@ begin
     FreeAndNil(MouseEvt);
   end;
 
-  if length(NewHoverElements)>0 then
+  if MouseEventId=evtMouseLeave then
   begin
-    El:=NewHoverElements[0];
-    WSData.ControlPos:=PageToContentPos(El,NewPageXY);
-  end else begin
-    El:=Self;
-    WSData.ControlPos:=WSData.PagePos;
-  end;
-
-  case MouseEventId of
-  evtMouseDown:
-    VPApplication.SetMouseDownElement(El,NewPageXY);
-  evtMouseLeave:
+    // mouse left the form
     VPApplication.SetMouseDownElement(nil,NewPageXY);
-  end;
-
-  if MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove] then
+  end else if MouseEventId in [evtMouseDown,evtMouseUp,evtMouseMove] then
   begin
-    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;
+    // these events have ClientPos and are affected by pointer capture
 
-        FreeAndNil(MouseEvt);
+    CaptEl:=GetPointerCaptureEl(nil);
+    if CaptEl<>nil then
+      El:=CaptEl
+    else if length(NewHoverElements)>0 then
+    begin
+      El:=NewHoverElements[0];
+      case Hit of
+      THit.hScrollbarHorz: El:=El.ScrollBarHorizontal;
+      THit.hScrollbarVert: El:=El.ScrollBarVertical;
+      end;
+    end else
+      El:=Self;
+    if El is TPseudoElement then
+      WSData.ClientPos:=ViewportToClientPos(El.Parent,NewViewportXY)
+    else
+      WSData.ClientPos:=ViewportToClientPos(El,NewViewportXY);
+
+    //writeln('TFresnelViewport.WSMouseXY ',El.GetPath,' VP=',WSData.ViewportPos.ToString,' ClientPos=',WSData.ClientPos.ToString);
+    //BubbleEl:=El;
+    //while BubbleEl<>nil do begin
+    //  writeln('  ',BubbleEl.Name,' Border=',BubbleEl.UsedBorderBox.ToString,' Content=',BubbleEl.UsedContentBox.ToString,' Client=',BubbleEl.UsedClientBox.ToString,' Scroll=',FloatToCSSStr(BubbleEl.ScrollLeft),',',FloatToCSSStr(BubbleEl.ScrollTop));
+    //  BubbleEl:=BubbleEl.Parent;
+    //end;
+
+    if MouseEventId=evtMouseDown then
+      VPApplication.SetMouseDownElement(El,NewPageXY);
+
+    MouseEvt:=nil;
+    ClickEvt:=nil;
+    try
+      // Dispatch mouse down/move/up events up the element hierarchy
+      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);
+        if MouseEvt.PropagationStopped then break;
+        BubbleEl:=BubbleEl.Parent;
+        if BubbleEl=nil then break;
+        if MouseEvt is TBubbleMouseDownEvent then
+          TBubbleMouseDownEvent(MouseEvt).SetXY(ViewportToClientPos(BubbleEl,NewViewportXY))
+        else if MouseEvt is TBubbleMouseUpEvent then
+          TBubbleMouseUpEvent(MouseEvt).SetXY(ViewportToClientPos(BubbleEl,NewViewportXY))
+        else if MouseEvt is TBubbleMouseMoveEvent then
+          TBubbleMouseMoveEvent(MouseEvt).SetXY(ViewportToClientPos(BubbleEl,NewViewportXY))
+        else
+          break;
+      until false;
 
+      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;
 
-        // 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;
+      if (MouseEventId=evtMouseUp) and (VPApplication.GetMouseDownElement(OldViewportXY)=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;
     end;
   end;
 end;
@@ -7580,35 +7646,35 @@ end;
 
 procedure TFresnelViewport.TBubbleMouseClickEvent.SetXY(const XY: TFresnelPoint);
 begin
-  FMouseInit.ControlPos:=XY;
+  FMouseInit.ClientPos:=XY;
 end;
 
 { TFresnelViewport.TBubbleMouseDoubleClickEvent }
 
 procedure TFresnelViewport.TBubbleMouseDoubleClickEvent.SetXY(const XY: TFresnelPoint);
 begin
-  FMouseInit.ControlPos:=XY;
+  FMouseInit.ClientPos:=XY;
 end;
 
 { TFresnelViewport.TBubbleMouseDownEvent }
 
 procedure TFresnelViewport.TBubbleMouseDownEvent.SetXY(const XY: TFresnelPoint);
 begin
-  FMouseInit.ControlPos:=XY;
+  FMouseInit.ClientPos:=XY;
 end;
 
 { TFresnelViewport.TBubbleMouseMoveEvent }
 
 procedure TFresnelViewport.TBubbleMouseMoveEvent.SetXY(const XY: TFresnelPoint);
 begin
-  FMouseInit.ControlPos:=XY;
+  FMouseInit.ClientPos:=XY;
 end;
 
 { TFresnelViewport.TBubbleMouseUpEvent }
 
 procedure TFresnelViewport.TBubbleMouseUpEvent.SetXY(const XY: TFresnelPoint);
 begin
-  FMouseInit.ControlPos:=XY;
+  FMouseInit.ClientPos:=XY;
 end;
 
 { TFresnelElement }
@@ -7661,21 +7727,13 @@ begin
 end;
 
 function TFresnelElement.GetClientWidth: TFresnelLength;
-var
-  n: TFresnelLayoutNode;
 begin
-  n:=LayoutNode;
-  Result:=n.Width-n.ScrollGutterLeft-n.ScrollGutterRight;
-  if Result<0 then Result:=0;
+  Result:=FUsedClientBox.Width;
 end;
 
 function TFresnelElement.GetClientHeight: TFresnelLength;
-var
-  n: TFresnelLayoutNode;
 begin
-  n:=LayoutNode;
-  Result:=n.Height-n.ScrollGutterTop-n.ScrollGutterBottom;
-  if Result<0 then Result:=0;
+  Result:=FUsedClientBox.Height;
 end;
 
 function TFresnelElement.GetViewportConnected: boolean;
@@ -8496,14 +8554,18 @@ end;
 function TFresnelElement.GetContainerOffset: TFresnelPoint;
 var
   Container, El: TFresnelElement;
+  c: TFresnelRect;
 begin
   Result:=Default(TFresnelPoint);
   Container:=LayoutNode.Container;
   El:=Parent;
   while El<>Container do
   begin
-    Result.X:=Result.X+El.UsedContentBox.Left;
-    Result.Y:=Result.Y+El.UsedContentBox.Top;
+    // Note: scrolling creates a container, so it can be ignored and
+    // it must be ignored as this is used by the layouter
+    c:=El.UsedContentBox;
+    Result.X:=Result.X+c.Left;
+    Result.Y:=Result.Y+c.Top;
     El:=El.Parent;
   end;
 end;
@@ -8548,34 +8610,62 @@ begin
   Result:=false;
 end;
 
+function TFresnelElement.GetUsedPaddingBox: TFresnelRect;
+var
+  Node: TFresnelLayoutNode;
+begin
+  Result:=UsedBorderBox;
+  Node:=LayoutNode;
+  Result.Left+=Node.BorderLeft;
+  Result.Top+=Node.BorderTop;
+  Result.Right-=Node.BorderRight;
+  Result.Bottom-=Node.BorderBottom;
+end;
+
 function TFresnelElement.GetBorderBoxOnViewport: TFresnelRect;
 var
   Node: TFresnelLayoutNode;
+  El: TFresnelElement;
+  c: TFresnelRect;
+  x, y: TFresnelLength;
 begin
-  if (not Rendered) then
+  Node:=LayoutNode;
+  if (Node=nil) or Node.SkipRendering then
   begin
     Result.Clear;
     exit;
   end;
-  Result:=RenderedBorderBox;
-  Node:=LayoutNode.Parent;
+  x:=0;
+  y:=0;
+  Node:=Node.Parent;
   while Node<>nil do begin
-    Result.Offset(Node.Element.RenderedContentBox.TopLeft);
+    El:=Node.Element;
+    c:=El.UsedClientBox;
+    x+=c.Left-El.ScrollLeft;
+    y+=c.Top-El.ScrollTop;
     Node:=Node.Parent;
   end;
+  Result:=UsedBorderBox;
+  Result.Offset(x,y);
   //debugln(['TFresnelElement.GetBoundingClientRect ',Name,' Result=',Result.Left,',',Result.Top,',',Result.Right,',',Result.Bottom]);
 end;
 
-function TFresnelElement.GetRenderedPaddingBox: TFresnelRect;
+function TFresnelElement.ClientToBorderPos(const X, Y: TFresnelLength): TFresnelPoint;
 var
   N: TFresnelLayoutNode;
 begin
-  Result:=RenderedContentBox;
   N:=LayoutNode;
-  Result.Left:=Result.Left-N.PaddingLeft;
-  Result.Top:=Result.Top-N.PaddingTop;
-  Result.Right:=Result.Right+N.PaddingRight;
-  Result.Bottom:=Result.Bottom+N.PaddingBottom;
+  Result.X:=X+N.ScrollGutterLeft+N.PaddingLeft+N.BorderLeft;
+  Result.Y:=Y+N.ScrollGutterTop+N.PaddingTop+N.BorderTop;
+end;
+
+function TFresnelElement.BorderToClientPos(const X, Y: TFresnelLength): TFresnelPoint;
+var
+  N: TFresnelLayoutNode;
+begin
+  N:=LayoutNode;
+  Result.X:=X-N.ScrollGutterLeft-N.PaddingLeft-N.BorderLeft;
+  Result.Y:=Y-N.ScrollGutterTop-N.PaddingTop-N.BorderTop;
 end;
 
 function TFresnelElement.GetComputedBorderRadius(Corner: TFresnelCSSCorner
@@ -9505,9 +9595,6 @@ end;
 
 procedure TFresnelElement.BeforeRender;
 begin
-  FRenderedBorderBox:=FUsedBorderBox;
-  FRenderedContentBox:=FUsedContentBox;
-
   If Assigned(FBeforeRender) then
     FBeforeRender(Self);
 end;

+ 6 - 6
src/base/fresnel.edit.pp

@@ -416,7 +416,7 @@ var
 begin
   if not (mbMain in aEvent.Buttons) then
     exit;
-  lNewPos:=CalcCharOffset(aEvent.ControlX);
+  lNewPos:=CalcCharOffset(aEvent.ClientX);
   if FCursorPos<>lNewPos then
     begin
     NormalizeSelection;
@@ -446,7 +446,7 @@ procedure TEdit.HandleMouseDown(aEvent: TFresnelMouseEvent);
 
 begin
   MaybeRecalcParams;
-  FCursorPos:=CalcCharOffset(aEvent.ControlX);
+  FCursorPos:=CalcCharOffset(aEvent.ClientX);
   if aEvent.ShiftKey then
     begin
     FSelectionEnd:=FCursorPos;
@@ -561,7 +561,7 @@ begin
   lLeftSideMargin:=GetComputedLength(fcaPaddingLeft);
   lRightSideMargin:=GetComputedLength(fcaPaddingRight);
   lVisibleStartX  := lLeftSideMargin;
-  lVisibleEndX    := RenderedContentBox.Width - lRightSideMargin;
+  lVisibleEndX    := ClientWidth - lRightSideMargin;
   FSelectionStartX := lVisibleEndX; // because we stop the search
   FSelectionEndX   := lVisibleEndX; // after last visible character is found
   bestfx := -MaxInt + 1 + lVisibleStartX;
@@ -723,7 +723,7 @@ begin
   lLeftSideMargin:=GetComputedLength(fcaPaddingLeft);
   lRightSideMargin:=GetComputedLength(fcaPaddingRight);
   lCursorX:=CalcXOffset(FCursorPos,False,True);
-  r := RenderedContentBox;
+  r := UsedClientBox;
   lVisibleWidth := (r.Width - (lLeftSideMargin+lRightSideMargin));
   lMaxWidth:=(lCursorX - (lVisibleWidth + CursorWidth) ); // 2 = Cursor width
   if FTextOffset < lMaxWidth  then
@@ -879,7 +879,7 @@ var
   var
     lCur : TFresnelRect;
   begin
-    lCur:=RenderedContentBox;
+    lCur:=UsedClientBox;
     lCur.Left:=lCur.Left+FCursorX; // CursorX has leftmargin
     lCur.Right:=lCur.Left+CursorWidth;
     aRenderer.FillRect(colBlack,lCur);
@@ -900,7 +900,7 @@ begin
   lBackColor:=GetComputedColor(fcaBackgroundColor,colTransparent);
   if lColorFP.Alpha=alphaTransparent then
     exit;
-  R:=RenderedContentBox;
+  R:=UsedClientBox;
   aRenderer.FillRect(lBackColor,R);
   // Selection background.
   RSel:=R;

+ 12 - 6
src/base/fresnel.events.pas

@@ -141,10 +141,14 @@ Type
   TFresnelMouseEventInit = Record
     Button: TMouseButton;
     Buttons: TMouseButtons;
-    PagePos: TFresnelPoint;
+    PagePos: TFresnelPoint; // document coords
+    ViewportPos: TFresnelPoint; // viewport coords
     ScreenPos: TFresnelPoint;
+    ClientPos: TFresnelPoint; // relative to target element's client box
     Shiftstate: TShiftState;
-    ControlPos: TFresnelPoint;
+    // pointer events
+    PointerId: TFreHandle;
+
     Function Description : String;
   end;
 
@@ -158,14 +162,16 @@ Type
   Public
     Constructor Create(const aInit : TFresnelMouseEventInit); overload;
     Procedure InitEvent(const aInit : TFresnelMouseEventInit);
-    Property ControlX : TFresnelLength Read FMouseInit.ControlPos.X;
-    Property ControlY : TFresnelLength Read FMouseInit.ControlPos.Y;
+    Property ClientX : TFresnelLength Read FMouseInit.ClientPos.X; // relative to target element's client box
+    Property ClientY : TFresnelLength Read FMouseInit.ClientPos.Y;
     Property PageX : TFresnelLength Read FMouseInit.PagePos.X; // document relative
     Property PageY : TFresnelLength Read FMouseInit.PagePos.Y;
     Property ScreenX : TFresnelLength Read FMouseInit.ScreenPos.X; // screen relative
     Property ScreenY : TFresnelLength Read FMouseInit.ScreenPos.Y;
-    Property X : TFresnelLength Read FMouseInit.ControlPos.X; // relative to element receiving the event
-    Property Y : TFresnelLength Read FMouseInit.ControlPos.Y;
+    Property ViewportX : TFresnelLength Read FMouseInit.ViewportPos.X; // viewport relative
+    Property ViewportY : TFresnelLength Read FMouseInit.ViewportPos.Y;
+    Property X : TFresnelLength Read FMouseInit.ClientPos.X; // same as ClientX
+    Property Y : TFresnelLength Read FMouseInit.ClientPos.Y; // same as ClientY
     Property Buttons: TMouseButtons Read FMouseInit.Buttons;
     Property Button : TMouseButton Read FMouseInit.Button;
     Property ShiftState : TShiftState Read FMouseInit.Shiftstate;

+ 8 - 7
src/base/fresnel.forms.pas

@@ -250,11 +250,11 @@ type
     FEventDispatcher: TFresnelEventDispatcher;
     FHoverElements: TFresnelElementArray;
     fMouseDownElement: TFresnelElement;
-    fMouseDownPageXY: TFresnelPoint;
+    fMouseDownViewportXY: TFresnelPoint;
     function GetHoverElements: TFresnelElementArray;
     procedure SetHoverElements(const AValue: TFresnelElementArray);
-    function GetMouseDownElement(out PageXY: TFresnelPoint): TFresnelElement;
-    procedure SetMouseDownElement(El: TFresnelElement; const PageXY: TFresnelPoint);
+    function GetMouseDownElement(out ViewportXY: TFresnelPoint): TFresnelElement;
+    procedure SetMouseDownElement(El: TFresnelElement; const ViewportXY: TFresnelPoint);
   protected
     procedure DoFresnelLog(aType: TEventType; const Msg: String); virtual;
     function GetHookFresnelLog: Boolean; virtual;
@@ -881,17 +881,18 @@ begin
   end;
 end;
 
-function TFresnelBaseApplication.GetMouseDownElement(out PageXY: TFresnelPoint): TFresnelElement;
+function TFresnelBaseApplication.GetMouseDownElement(out ViewportXY: TFresnelPoint
+  ): TFresnelElement;
 begin
   Result:=fMouseDownElement;
-  PageXY:=fMouseDownPageXY;
+  ViewportXY:=fMouseDownViewportXY;
 end;
 
 procedure TFresnelBaseApplication.SetMouseDownElement(El: TFresnelElement;
-  const PageXY: TFresnelPoint);
+  const ViewportXY: TFresnelPoint);
 begin
   fMouseDownElement:=El;
-  fMouseDownPageXY:=PageXY;
+  fMouseDownViewportXY:=ViewportXY;
 end;
 
 procedure TFresnelBaseApplication.DoFresnelLog(aType: TEventType; const Msg: String);

+ 14 - 0
src/base/fresnel.layouter.pas

@@ -258,6 +258,7 @@ var
   Size: TFresnelPoint;
   HasMaxWidth, HasMaxHeight: Boolean;
   El: TFresnelElement;
+  aClientBox: TFresnelRect;
 begin
   El:=Node.Element;
 
@@ -286,6 +287,13 @@ begin
       {$ENDIF}
     end;
 
+    aClientBox:=El.UsedContentBox;
+    aClientBox.Left+=Node.ScrollGutterLeft;
+    aClientBox.Top+=Node.ScrollGutterTop;
+    aClientBox.Right-=Node.ScrollGutterRight;
+    aClientBox.Bottom-=Node.ScrollGutterBottom;
+    El.UsedClientBox:=aClientBox;
+
     aClientWidth:=MaxWidth;
     if HasMaxWidth then
       aClientWidth:=aClientWidth-Node.ScrollGutterLeft-Node.ScrollGutterRight;
@@ -535,6 +543,7 @@ var
 
 var
   aSize: TFresnelPoint;
+  OldNeedVert: Boolean;
 begin
   case aMode of
     flmMinWidth:  aMaxWidth:=NaN;
@@ -593,8 +602,10 @@ begin
         if HasMaxWidth and (Result.Width>aMaxWidth) and Node.CanScrollX then
         begin
           // width no longer fits -> compute again
+          OldNeedVert:=Result.NeedGutterVertical;
           Compute(flmMax,aClientWidth,aClientHeight,true);
           Result.NeedGutterHorizontal:=true;
+          Result.NeedGutterVertical:=OldNeedVert;
           CheckOverflowX;
           CheckOverflowY;
         end;
@@ -1961,6 +1972,7 @@ begin
 
     ChildEl.UsedBorderBox:=aBorderBox;
     ChildEl.UsedContentBox:=aContentBox;
+    ChildEl.UsedClientBox:=aContentBox;
     ChildNode.Left:=NewLeft;
     ChildNode.Top:=NewTop;
     ChildNode.Right:=aBorderBox.Right+ChildNode.MarginRight;
@@ -2101,6 +2113,7 @@ begin
     r.Right:=r.Left+NewWidth;
     r.Bottom:=R.Top+NewHeight;
     ChildEl.UsedContentBox:=r;
+    ChildEl.UsedClientBox:=r;
 
     {$IFDEF VerboseFresnelPlacing}
     writeln('TFLLineLayouter.PlaceAbsoluteItem '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
@@ -2290,6 +2303,7 @@ begin
   r:=TFresnelRect.Create(0,0,Viewport.Width,Viewport.Height);
   Viewport.UsedBorderBox:=r;
   Viewport.UsedContentBox:=r;
+  Viewport.UsedClientBox:=r;
 
   // update layout nodes and compute used lengths (top-down)
   UpdateLayoutNodes(Viewport);

+ 85 - 100
src/base/fresnel.renderer.pas

@@ -36,12 +36,14 @@ type
   { TRendererScrollBar - simple custom drawn scrollbar with a round thumb bar - no buttons }
 
   TRendererScrollBar = class(TPseudoElScrollBar)
+  protected
+    procedure MouseHandler(Event: TAbstractEvent); virtual;
   public
     Border: TFresnelLength;
+    constructor Create(AOwner: TComponent); override; overload;
     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 }
@@ -389,6 +391,75 @@ end;
 
 { TRendererScrollBar }
 
+procedure TRendererScrollBar.MouseHandler(Event: TAbstractEvent);
+var
+  MouseEvt: TFresnelMouseEvent absolute Event;
+  X, Y, NewPos: TFresnelLength;
+begin
+  // the mouse event has the coord of the Parent's clientbox
+  // -> translate to GrandParent's child box
+  X:=MouseEvt.ClientX+Parent.UsedClientBox.Left;
+  Y:=MouseEvt.ClientY+Parent.UsedClientBox.Top;
+  {$IFDEF VerboseFresnelScrolling}
+  writeln('TRendererScrollBar.MouseHandler ',Parent.Name,' ClientBox=',Parent.UsedClientBox.ToString,' Box=',Box.ToString,' X=',FloatToCSSStr(X),',',FloatToCSSStr(Y),' Size=',FloatToCSSStr(Size),' Position=',FloatToCSSStr(Position),' Page=',FloatToCSSStr(Page));
+  {$ENDIF}
+  MouseEvt.StopPropagation;
+
+  case MouseEvt.EventID of
+  evtMouseDown:
+    if MouseEvt.Button=mbLeft then
+    begin
+      MouseDownHit:=GetHit(X,Y,MouseDownPos);
+      {$IFDEF VerboseFresnelScrolling}
+      writeln('TRendererScrollBar.MouseHandler Mouse Down ',Parent.Name,' Horz=',Horizontal,' Hit=',MouseDownHit,' p=',FloatToCSSStr(MouseDownPos));
+      {$ENDIF}
+      case MouseDownHit of
+      THit.hButtonLT: ;
+      THit.hButtonRB: ;
+      THit.hThumb:
+        begin
+          exit;
+        end;
+      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 Parent.ComputedDirection=CSSRegistry.kwRTL then
+          NewPos:=NewPos-Size+Page;
+        Parent.ScrollLeft:=NewPos;
+      end
+      else
+        Parent.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 (MouseEvt.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;
+
+constructor TRendererScrollBar.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  AddEventListener(evtMouseDown,@MouseHandler);
+  AddEventListener(evtMouseMove,@MouseHandler);
+  AddEventListener(evtMouseUp,@MouseHandler);
+end;
+
 procedure TRendererScrollBar.Draw(Renderer: TFresnelRenderer);
 var
   TrackColor, ThumbColor: TFPColor;
@@ -403,7 +474,7 @@ begin
 
   // draw track
   TrackColor:=colLtGray;
-  if not IsHovered then
+  if not Parent.IsHovered then
     TrackColor.Alpha:=$8000;
   Renderer.FillRect(TrackColor,r);
   if Size<1 then
@@ -486,6 +557,8 @@ begin
   if aPosition>1 then aPosition:=1;
   aPosition:=aPosition*Size;
 
+  writeln('TRendererScrollBar.GetHit Horz=',Horizontal,' aPosition=',FloatToCSSStr(aPosition),' Position=',FloatToCSSStr(Position),' Size=',FloatToCSSStr(Size),' LT=',FloatToCSSStr(LT),' RB=',FloatToCSSStr(RB),' Y=',FloatToCSSStr(Y));
+
   if aPosition<Position then
     Result:=THit.hTrackLT
   else if aPosition>=Position+Page then
@@ -494,64 +567,6 @@ begin
     Result:=THit.hThumb;
 end;
 
-procedure TRendererScrollBar.MouseHandler(WSData: TFresnelMouseEventInit; MouseEventId: TEventID);
-var
-  X, Y, NewPos: TFresnelLength;
-begin
-  X:=WSData.ControlPos.X+Parent.RenderedContentBox.Left;
-  Y:=WSData.ControlPos.Y+Parent.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 ',Parent.Name,' Hit=',MouseDownHit,' p=',FloatToCSSStr(MouseDownPos));
-      {$ENDIF}
-      case MouseDownHit of
-      THit.hButtonLT: ;
-      THit.hButtonRB: ;
-      THit.hThumb:
-        begin
-          //SetPointerCapture(wsda);
-          exit;
-        end;
-      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 Parent.ComputedDirection=CSSRegistry.kwRTL then
-          NewPos:=NewPos-Size+Page;
-        Parent.ScrollLeft:=NewPos;
-      end
-      else
-        Parent.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 }
 
 procedure TFresnelRenderer.SetOrigin(const AValue: TFresnelPoint);
@@ -709,7 +724,7 @@ begin
     CSSRegistry.kwThin: BarHeight:=El.Viewport.ScrollbarsThinWidth[true];
     else BarHeight:=El.Viewport.ScrollbarsWidth[true];
     end;
-  aPaddingBox:=El.GetRenderedPaddingBox;
+  aPaddingBox:=El.GetUsedPaddingBox;
   //writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' PaddingBox=',aPaddingBox.ToString);
 
   aScrollbar:=El.ScrollBarVertical as TRendererScrollBar;
@@ -735,7 +750,7 @@ begin
       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);
+      writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' Vertical ScrollHeight=',FloatToCSSStr(El.ScrollHeight),' ClientHeight=',FloatToCSSStr(El.ClientHeight),' ScrollTop=',FloatToCSSStr(El.ScrollTop)+' BarWidth='+FloatToCSSStr(BarWidth)+' r='+r.ToString);
       {$ENDIF}
       aScrollbar.Draw(Self);
     end;
@@ -770,7 +785,7 @@ begin
       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));
+      writeln('TFresnelRenderer.DrawScrollBars ',El.Name,' Horizontal ScrollWidth=',FloatToCSSStr(El.ScrollWidth),' ClientWidth=',FloatToCSSStr(El.ClientWidth),' ScrollLeft=',FloatToCSSStr(El.ScrollLeft)+' BarHeight='+FloatToCSSStr(BarHeight)+' r='+r.ToString);
       {$ENDIF}
       aScrollbar.Draw(Self);
     end;
@@ -797,14 +812,13 @@ end;
 procedure TFresnelRenderer.DrawElement(El: TFresnelElement);
 var
   LNode: TUsedLayoutNode;
-  aBorderBox, aContentBox, r: TFresnelRect;
+  aBorderBox, r: TFresnelRect;
   BorderParams: TBorderAndBackground;
   aRenderable : IFresnelRenderable;
   s: TFresnelCSSSide;
   Corner: TFresnelCSSCorner;
   aClientWidth, aClientHeight: TFresnelLength;
   ClipHorizontal, ClipVertical, NeedRestore: Boolean;
-  OldOrigin, NewOrigin: TFresnelPoint;
 begin
   FLLog(etDebug,'TFresnelRenderer.DrawElement %s Origin=%s',[El.GetPath,Origin.ToString]);
   LNode:=TUsedLayoutNode(El.LayoutNode);
@@ -814,10 +828,6 @@ begin
   El.Rendered:=true;
 
   aBorderBox:=El.UsedBorderBox;
-  El.RenderedBorderBox:=aBorderBox;
-
-  aContentBox:=El.UsedContentBox;
-  El.RenderedContentBox:=aContentBox;
 
   FLLog(etDebug,'TFresnelRenderer.DrawElement %s %s',[El.GetPath,aBorderBox.ToString]);
 
@@ -867,7 +877,7 @@ begin
   if ClipHorizontal or ClipVertical then
   begin
     //writeln('TFresnelRenderer.DrawElement ',El.Name,' ',ClipHorizontal,' ',ClipVertical);
-    r:=El.GetRenderedPaddingBox;
+    r:=El.GetUsedPaddingBox;
     if not ClipHorizontal then
     begin
       r.Left:=-Origin.X;
@@ -882,23 +892,15 @@ begin
     NeedRestore:=true;
     ClipRect(r);
   end;
-  OldOrigin:=Origin;
   try
-    NewOrigin:=OldOrigin;
-    NewOrigin.X:=NewOrigin.X-El.ScrollLeft;
-    NewOrigin.Y:=NewOrigin.Y-El.ScrollTop;
-    Origin:=NewOrigin;
-
     // Give element a chance to draw itself (on top of background and border)
     aRenderable.Render(Self as IFresnelRenderer);
 
     DrawChildren(El);
-    Origin:=OldOrigin;
 
     // draw scrollbars
     DrawScrollBars(El);
   finally
-    Origin:=OldOrigin;
     if NeedRestore then
       Restore;
   end;
@@ -907,7 +909,7 @@ end;
 
 procedure TFresnelRenderer.DrawChildren(El: TFresnelElement);
 var
-  OldOrigin: TFresnelPoint;
+  OldOrigin, NewOrigin: TFresnelPoint;
   LNode: TUsedLayoutNode;
   i: Integer;
   ChildEl: TFresnelElement;
@@ -915,7 +917,9 @@ begin
   LNode:=TUsedLayoutNode(El.LayoutNode);
 
   OldOrigin:=Origin;
-  Origin:=OldOrigin+El.RenderedContentBox.TopLeft;
+  NewOrigin.X:=OldOrigin.X+El.UsedClientBox.Left-El.ScrollLeft;
+  NewOrigin.Y:=OldOrigin.Y+El.UsedClientBox.Left-El.ScrollTop;
+  Origin:=NewOrigin;
   //writeln('TFresnelRenderer.DrawChildren ',El.GetPath,' Old=',OldOrigin.ToString,' Origin=',Origin.ToString);
   for i:=0 to LNode.NodeCount-1 do
   begin
@@ -928,35 +932,16 @@ end;
 
 procedure TFresnelRenderer.Draw(Viewport: TFresnelViewport);
 var
-  aBorderBox, aContentBox: TFresnelRect;
   BackgroundColorFP: TFPColor;
   aRenderable: IFresnelRenderable;
-  LNode: TFresnelLayoutNode;
 begin
   //debugln(['TFresnelRenderer.Draw Origin=',dbgs(Origin)]);
-  aBorderBox.Left:=0;
-  aBorderBox.Top:=0;
-  aBorderBox.Right:=Viewport.Width;
-  aBorderBox.Bottom:=Viewport.Height;
-  Viewport.UsedBorderBox:=aBorderBox;
-
-  aContentBox:=aBorderBox;
-  LNode:=Viewport.LayoutNode;
-  if LNode<>nil then
-  begin
-    aContentBox.Left:=LNode.ScrollGutterLeft;
-    aContentBox.Top:=LNode.ScrollGutterTop;
-    aContentBox.Right:=Max(aContentBox.Left,aContentBox.Right-LNode.ScrollGutterRight);
-    aContentBox.Bottom:=Max(aContentBox.Top,aContentBox.Bottom-LNode.ScrollGutterBottom);
-  end;
-  Viewport.UsedContentBox:=aContentBox;
-
   aRenderable:=Viewport as IFresnelRenderable;
   aRenderable.BeforeRender;
   Viewport.Rendered:=true;
 
   BackgroundColorFP:=Viewport.GetComputedColor(fcaBackgroundColor,colWhite);
-  FillRect(BackgroundColorFP,aBorderBox);
+  FillRect(BackgroundColorFP,RectFre(0,0,Viewport.Width,Viewport.Height));
 
   // todo: draw scrollbars
 

+ 4 - 2
src/gtk3/fresnel.gtk3.pas

@@ -724,11 +724,13 @@ var
 begin
   Result:=false;
   WSData:=Default(TFresnelMouseEventInit);
+  WSData.PointerId:=Gtk3WidgetSet;
   MButton := Event^.button.button;
 
   // todo: WSData.ScreenPos
-  WSData.PagePos.X:=Event^.button.x;
-  WSData.PagePos.Y:=Event^.button.y;
+  WSData.ViewportPos.X:=Event^.button.x;
+  WSData.ViewportPos.Y:=Event^.button.y;
+  WSData.PagePos:=WSData.ViewportPos;
 
   WSData.Shiftstate:=GdkModifierStateToShiftState(Event^.button.state);
 

+ 3 - 2
src/lcl/fresnel.lcl.pas

@@ -449,8 +449,9 @@ begin
   if ssExtra2 in Shift then
     Include(EvtInit.Buttons,mbExtra2);
   EvtInit.ScreenPos.SetLocation(Controls.Mouse.CursorPos);
-  EvtInit.PagePos.X:=X;
-  EvtInit.PagePos.Y:=Y;
+  EvtInit.ViewportPos.X:=X;
+  EvtInit.ViewportPos.Y:=Y;
+  EvtInit.PagePos:=EvtInit.ViewportPos;
   EvtInit.Shiftstate:=Shift;
 end;
 

+ 3 - 2
src/wasm/fresnel.wasm.app.pp

@@ -281,8 +281,9 @@ begin
   if ssExtra2 in Shift then
     Include(EvtInit.Buttons,mbExtra2);
   EvtInit.ScreenPos.SetLocation(TFresnelPoint.Create(Data^[WASMSG_MOUSESTATE_X],Data^[WASMSG_MOUSESTATE_Y]));
-  EvtInit.PagePos.X:=EvtInit.ScreenPos.X;
-  EvtInit.PagePos.Y:=EvtInit.ScreenPos.Y;
+  EvtInit.ViewportPos.X:=EvtInit.ScreenPos.X;
+  EvtInit.ViewportPos.Y:=EvtInit.ScreenPos.Y;
+  EvtInit.PagePos:=EvtInit.ViewportPos;
   EvtInit.Shiftstate:=Shift;
 end;
 

+ 3 - 3
tests/LabelFragments/FreLabelFragsForm.pas

@@ -44,17 +44,17 @@ end;
 
 procedure TLabelFragmentsTestForm.Label1MouseDown(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseDown ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseDown ',Event.ClientX,',',Event.ClientY]);
 end;
 
 procedure TLabelFragmentsTestForm.Label1MouseMove(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseMove ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseMove ',Event.ClientX,',',Event.ClientY]);
 end;
 
 procedure TLabelFragmentsTestForm.Label1MouseUp(Event: TFresnelMouseEvent);
 begin
-  debugln(['TFresnelButtonForm.Label1MouseUp ',Event.ControlX,',',Event.ControlY]);
+  debugln(['TFresnelButtonForm.Label1MouseUp ',Event.ClientX,',',Event.ClientY]);
 end;
 
 end.