Browse Source

moving image update into image view

Johann ELSASS 5 năm trước cách đây
mục cha
commit
46078fdf0a

+ 34 - 1
lazpaint/lazpaintinstance.pas

@@ -108,6 +108,7 @@ type
     FInRunScript: boolean;
     FScriptTempFileNames: TStringList;
     FInCommandLine: boolean;
+    FUpdateStackOnTimer: boolean;
 
     function GetIcons(ASize: integer): TImageList; override;
     function GetToolBoxWindowPopup: TPopupMenu; override;
@@ -143,6 +144,8 @@ type
     function GetImage: TLazPaintImage; override;
     function GetImageAction: TImageActions; override;
     function GetToolManager: TToolManager; override;
+    function GetUpdateStackOnTimer: boolean; override;
+    procedure SetUpdateStackOnTimer(AValue: boolean); override;
     procedure CreateLayerStack;
     procedure CreateToolBox;
     procedure FormsNeeded;
@@ -202,6 +205,7 @@ type
     destructor Destroy; override;
     procedure NotifyImageChange(RepaintNow: boolean; ARect: TRect); override;
     procedure NotifyImageChangeCompletely(RepaintNow: boolean); override;
+    procedure NotifyImagePaint; override;
     function TryOpenFileUTF8(filename: string; skipDialogIfSingleImage: boolean = false): boolean; override;
     function ExecuteFilter(filter: TPictureFilter; skipDialog: boolean = false): TScriptResult; override;
     function RunScript(AFilename: string): boolean; override;
@@ -247,6 +251,8 @@ type
     property MainFormVisible: boolean read GetMainFormVisible;
     procedure NotifyStackChange; override;
     procedure ScrollLayerStackOnItem(AIndex: integer; ADelayedUpdate: boolean = true); override;
+    procedure InvalidateLayerStack; override;
+    procedure UpdateLayerStackOnTimer; override;
     function MakeNewBitmapReplacement(AWidth, AHeight: integer; AColor: TBGRAPixel): TBGRABitmap; override;
     procedure ChooseTool(Tool : TPaintToolType); override;
     function OpenImage (FileName: string; AddToRecent: Boolean= True): boolean; override;
@@ -497,6 +503,16 @@ begin
   Result:= FToolManager;
 end;
 
+function TLazPaintInstance.GetUpdateStackOnTimer: boolean;
+begin
+  result := FUpdateStackOnTimer;
+end;
+
+procedure TLazPaintInstance.SetUpdateStackOnTimer(AValue: boolean);
+begin
+  FUpdateStackOnTimer := AValue;
+end;
+
 procedure TLazPaintInstance.CreateLayerStack;
 var
   defaultZoom: Single;
@@ -1580,6 +1596,12 @@ begin
   If RepaintNow then FMain.Update;
 end;
 
+procedure TLazPaintInstance.NotifyImagePaint;
+begin
+  if Assigned(FMain) then
+    FMain.NotifyImagePaint;
+end;
+
 function TLazPaintInstance.TryOpenFileUTF8(filename: string; skipDialogIfSingleImage: boolean): boolean;
 begin
   FormsNeeded;
@@ -1778,10 +1800,21 @@ begin
   begin
     if not Assigned(FMain) then ADelayedUpdate:= false;
     FLayerStack.ScrollToItem(AIndex, not ADelayedUpdate);
-    if ADelayedUpdate then FMain.UpdateStackOnTimer := true;
+    if ADelayedUpdate then UpdateStackOnTimer := true;
   end;
 end;
 
+procedure TLazPaintInstance.InvalidateLayerStack;
+begin
+  if FLayerStack<> nil then
+    FLayerStack.InvalidateStack(false);
+end;
+
+procedure TLazPaintInstance.UpdateLayerStackOnTimer;
+begin
+  UpdateStackOnTimer := true;
+end;
+
 function TLazPaintInstance.MakeNewBitmapReplacement(AWidth, AHeight: integer; AColor: TBGRAPixel): TBGRABitmap;
 begin
   result := TBGRABitmap.Create(AWidth,AHeight, AColor);

+ 0 - 23
lazpaint/lazpaintmainform.lfm

@@ -7,7 +7,6 @@ object FMain: TFMain
   Caption = 'LazPaint'
   ClientHeight = 572
   ClientWidth = 739
-  Color = clSkyBlue
   Constraints.MinWidth = 320
   Font.Height = -12
   KeyPreview = True
@@ -20,12 +19,6 @@ object FMain: TFMain
   OnHide = FormHide
   OnKeyDown = FormKeyDown
   OnKeyUp = FormKeyUp
-  OnMouseDown = FormMouseDown
-  OnMouseEnter = FormMouseEnter
-  OnMouseMove = FormMouseMove
-  OnMouseUp = FormMouseUp
-  OnMouseWheel = FormMouseWheel
-  OnPaint = FormPaint
   OnResize = FormResize
   OnShow = FormShow
   OnUTF8KeyPress = FormUTF8KeyPress
@@ -2342,22 +2335,6 @@ object FMain: TFMain
       end
     end
   end
-  object PaintBox_Picture: TPaintBox
-    Left = 608
-    Height = 84
-    Top = 56
-    Width = 117
-    Anchors = [akTop, akLeft, akRight, akBottom]
-    Font.Height = -12
-    ParentFont = False
-    Visible = False
-    OnMouseDown = PaintBox_PictureMouseDown
-    OnMouseEnter = PaintBox_PictureMouseEnter
-    OnMouseMove = PaintBox_PictureMouseMove
-    OnMouseUp = PaintBox_PictureMouseUp
-    OnMouseWheel = PaintBox_PictureMouseWheel
-    OnPaint = PaintBox_PicturePaint
-  end
   object Panel_Brush: TPanel
     Left = 8
     Height = 36

+ 93 - 461
lazpaint/lazpaintmainform.pas

@@ -5,10 +5,6 @@ unit LazpaintMainForm;
 
 interface
 
-{$IFDEF DARWIN}
-  {$DEFINE USEPAINTBOXPICTURE}
-{$ENDIF}
-
 uses
   Classes, LMessages, SysUtils, LazFileUtils, LResources, Forms, Controls,
   Graphics, Dialogs, Menus, ExtDlgs, ComCtrls, ActnList, StdCtrls, ExtCtrls,
@@ -21,7 +17,7 @@ uses
   ubrowseimages, UToolPolygon, UToolVectorial, LCVectorRectShapes,
   LCVectorialFillControl, LCVectorialFill,
 
-  laztablet, udarktheme, UScriptType;
+  udarktheme, UScriptType;
 
 type
   { TFMain }
@@ -345,7 +341,6 @@ type
     Panel_Text: TPanel;
     ToolBar15: TToolBar;
     Tool_TextFont: TToolButton;
-    PaintBox_Picture: TPaintBox;
     PaintBox_PenPreview: TPaintBox;
     Panel_Embedded: TPanel;
     Panel_PenWidthPreview: TPanel;
@@ -480,7 +475,6 @@ type
     procedure FileUseImageBrowserUpdate(Sender: TObject);
     procedure ForgetDialogAnswersExecute(Sender: TObject);
     procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
-    procedure FormMouseLeave(Sender: TObject);
     procedure FormWindowStateChange(Sender: TObject);
     procedure ItemDockLayersAndColorsClick(Sender: TObject);
     procedure ItemFullscreenClick(Sender: TObject);
@@ -518,7 +512,6 @@ type
     procedure FileSaveSelectionAsUpdate(Sender: TObject);
     procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
     procedure FormKeyUp(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
-    procedure FormMouseEnter(Sender: TObject);
     procedure FormUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
     procedure ImageCropLayerUpdate(Sender: TObject);
     procedure ImageFlattenExecute(Sender: TObject);
@@ -533,7 +526,6 @@ type
     procedure LayerRotateExecute(Sender: TObject);
     procedure LayerRotateUpdate(Sender: TObject);
     procedure ItemDonateClick(Sender: TObject);
-    procedure PaintBox_PictureMouseEnter(Sender: TObject);
     procedure Perspective_TwoPlanesClick(Sender: TObject);
     procedure SpinEdit_ShapeAltitudeChange(Sender: TObject; AByUser: boolean);
     procedure SpinEdit_BrushSpacingChange(Sender: TObject; AByUser: boolean);
@@ -545,15 +537,6 @@ type
     procedure ToolButton_DonateClick(Sender: TObject);
     procedure VectorialFill_TextureClick(Sender: TObject);
     procedure PaintBox_PenPreviewPaint(Sender: TObject);
-    procedure PaintBox_PictureMouseDown(Sender: TObject; Button: TMouseButton;
-      Shift: TShiftState; X, Y: Integer);
-    procedure PaintBox_PictureMouseMove(Sender: TObject; Shift: TShiftState; X,
-      Y: Integer);
-    procedure PaintBox_PictureMouseUp(Sender: TObject; Button: TMouseButton;
-      Shift: TShiftState; X, Y: Integer);
-    procedure PaintBox_PictureMouseWheel(Sender: TObject; Shift: TShiftState;
-      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
-    procedure PaintBox_PicturePaint(Sender: TObject);
     procedure Panel_PenWidthMouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X,
       {%H-}Y: Integer);
     procedure Panel_ToolbarBackgroundMouseMove(Sender: TObject;
@@ -566,8 +549,6 @@ type
     procedure ToolAnyExecute(Sender: TObject);
     procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
     procedure FormHide(Sender: TObject);
-    procedure FormMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
-      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
     procedure HelpAboutExecute(Sender: TObject);
     procedure HelpIndexExecute(Sender: TObject);
     procedure ImageChangeCanvasSizeExecute(Sender: TObject);
@@ -633,12 +614,6 @@ type
     procedure FormResize(Sender: TObject);
     procedure ImageActionExecute(Sender: TObject);
     procedure FormCreate(Sender: TObject);
-    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
-      Shift: TShiftState; X, Y: Integer);
-    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
-    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
-      Shift: TShiftState; X, Y: Integer);
-    procedure FormPaint(Sender: TObject);
     procedure FormShow(Sender: TObject);
     procedure Image_SwapColorsMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
@@ -688,6 +663,10 @@ type
     procedure ManagerToleranceChanged(Sender: TObject);
     procedure ManagerToolbarChanged(Sender: TObject);
     procedure Perspective_RepeatClick(Sender: TObject);
+    procedure PictureMouseBefore(Sender: TObject; AShift: TShiftState);
+    procedure PictureMouseMove(Sender: TObject; APosition: TPointF);
+    procedure PictureOnPaint(Sender: TObject);
+    procedure PictureToolbarUpdate(Sender: TObject);
     function ScriptShowColorDialog(AVars: TVariableSet): TScriptResult;
     procedure VectorialFill_Change(Sender: TObject);
     procedure VectorialFill_TypeChange(Sender: TObject);
@@ -711,9 +690,6 @@ type
     FBrowseBrushes: TFBrowseImages;
     FSaveImage: TFBrowseImages;
     FSaveSelection: TFBrowseImages;
-
-    FTablet: TLazTablet;
-
     FLoadInitialDir, FSaveInitialDir, FExportInitialDir: string;
     FSaveSelectionInitialFilename: string;
     FInFillChange, FInPenWidthChange, FInBrush, FInShapeRatio, FInEraserOption,
@@ -723,31 +699,20 @@ type
     FOnlineUpdater: TLazPaintCustomOnlineUpdater;
     FInitialized: boolean;
     FShouldArrange: boolean;
-    btnLeftDown, btnRightDown, btnMiddleDown: boolean;
     spacePressed, altPressed, snapPressed, shiftPressed: boolean;
-    FormMouseMovePos: TPoint;
-    InFormMouseMove: boolean;
-    InFormPaint: boolean;
     FirstPaint, LoadToolWindow: boolean;
-    CanCompressOrUpdateStack: boolean;
     FShowSelectionNormal: boolean;
     FLazPaintInstance: TLazPaintCustomInstance;
     Config: TLazPaintConfig;
     StartDirectory: string;
     previousToolImg: integer;
     currentToolLabel: string;
-    InShowNoPicture: boolean;
     FTopMostInfo: TTopMostInfo;
-    DelayedPaintPicture, CatchPaintPicture, PaintPictureCatched: boolean;
     Panel_LineCap_FullSize: integer;
     FCoordinatesCaption: string;
     FCoordinatesCaptionCount: NativeInt;
-    FImageView: TImageView;
-    FLastPaintDate: TDateTime;
-    FUpdateStackWhenIdle: boolean;
     FToolbarElementsInitDone: boolean;
 
-    function GetCurrentPressure: single;
     function GetDarkTheme: boolean;
     function GetImageAction: TImageActions;
     function GetUpdatingPopup: boolean;
@@ -789,7 +754,6 @@ type
 
     procedure CreateMenuAndToolbar;
     function GetToolManager: TToolManager;
-    procedure LayoutPictureAreaChange({%H-}ASender: TObject; {%H-}ANewArea: TRect);
     function GetCurrentTool: TPaintToolType;
     procedure SwitchColors;
     function EditingColors: boolean;
@@ -797,7 +761,6 @@ type
     procedure OnLatestVersionUpdate(ANewVersion: string);
     procedure ManagerToolChanged({%H-}sender: TToolManager; {%H-}ANewTool: TPaintToolType);
     procedure OnQueryExitToolHandler({%H-}sender: TLazPaintImage);
-    procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single);
     procedure SetLazPaintInstance(const AValue: TLazPaintCustomInstance);
     procedure SetShowSelectionNormal(const AValue: boolean);
     procedure ToggleToolwindowsVisible;
@@ -813,20 +776,15 @@ type
     procedure HidePenPreview(ATimeMs: Integer = 300; AClearTime: boolean = false);
     procedure ShowFill(AFillControl: TLCVectorialFillControl; APanel: TPanel);
     procedure HideFill(ATimeMs: Integer = 300; AClearTime: boolean = false);
-    procedure OnPaintHandler;
     procedure OnImageChangedHandler({%H-}AEvent: TLazPaintImageObservationEvent);
     procedure OnImageRenderChanged({%H-}Sender: TObject);
     procedure LabelAutosize(ALabel: TLabel);
     procedure AskMergeSelection(ACaption: string);
-    procedure ReleaseMouseButtons(Shift: TShiftState);
     procedure UpdateSpecialKeys({%H-}Shift: TShiftState);
     procedure UpdateCurveModeToolbar;
     function ShowOpenTextureDialog(ATargetFill: TVectorialFill): boolean;
     procedure ShowNoPicture;
     procedure SetCurveMode(AMode: TToolSplineMode);
-    procedure IncreasePenSize;
-    procedure DecreasePenSize;
-    function PenSizeDelta(direction: integer): integer;
     procedure UpdatePenWidthFromSpinEdit;
     procedure UpdateWindowCaption;
     procedure ImageCurrentFilenameChanged({%H-}sender: TLazPaintImage);
@@ -864,15 +822,15 @@ type
 
   public
     { public declarations }
-    UpdateStackOnTimer: boolean;
     Zoom: TZoom;
 
     procedure PaintPictureNow;
+    procedure PaintPictureLater;
+    procedure NotifyImagePaint;
     procedure InvalidatePicture(AInvalidateAll: boolean = true);
     function TryOpenFileUTF8(filenameUTF8: string; AddToRecent: Boolean=True;
       ALoadedImage: PImageEntry = nil; ASkipDialogIfSingleImage: boolean = false;
       AAllowDuplicate: boolean = false; AEntryToLoad: integer = -1): Boolean;
-    function PictureCanvasOfs: TPoint;
     procedure UpdateLineCapBar;
     procedure UpdateFillToolbar(AUpdateColorDiff: boolean);
     procedure UpdateToolbar;
@@ -890,7 +848,6 @@ type
     property ToolManager: TToolManager read GetToolManager;
     property Layout: TMainFormLayout read FLayout;
     property UseImageBrowser: boolean read GetUseImageBrowser;
-    property CurrentPressure: single read GetCurrentPressure;
     property DarkTheme: boolean read GetDarkTheme write SetDarkTheme;
     property Initialized: boolean read FInitialized;
     property UpdatingPopup: boolean read GetUpdatingPopup write SetUpdatingPopup;
@@ -915,17 +872,16 @@ procedure TFMain.FormCreate(Sender: TObject);
 begin
   FInitialized := false;
 
-  FLayout := TMainFormLayout.Create(self);
-  FImageView := nil;
+  Zoom := TZoom.Create(Label_CurrentZoom,Edit_Zoom,FLayout);
+  FLayout := TMainFormLayout.Create(self, Zoom);
+  FLayout.OnPaintPicture:=@PictureOnPaint;
+  FLayout.OnToolbarUpdate:=@PictureToolbarUpdate;
+  FLayout.OnPictureMouseMove:=@PictureMouseMove;
+  FLayout.OnPictureMouseBefore:=@PictureMouseBefore;
 
   ScaleControl(Self,OriginalDPI);
   self.Color := clBtnFace; //toolbar color inherited on mac
 
-  {$IFDEF USEPAINTBOXPICTURE}
-  PaintBox_Picture.SetBounds(0,0,ClientWidth,ClientHeight);
-  PaintBox_Picture.Visible := True;
-  {$ENDIF}
-
   //mac interface
   CheckActions(ActionList1);
   CheckQuitMenu(ItemQuit,ItemQuitSeparator);
@@ -944,29 +900,13 @@ begin
   ExportPictureDialog.Filter := SavePictureDialog1.Filter;
   SaveSelectionDialog.Filter := SavePictureDialog1.Filter;
 
-  Zoom := TZoom.Create(Label_CurrentZoom,Edit_Zoom,FLayout);
-  Zoom.OnZoomChanged:= @OnZoomChanged;
   previousToolImg:= -1;
 
-  //mouse status
-  btnLeftDown := false;
-  btnRightDown := false;
-  btnMiddleDown:= false;
-  try
-    FTablet := TLazTablet.Create(self);
-  except
-    on ex: exception do
-      FTablet := nil;
-  end;
   spacePressed:= false;
   altPressed:= false;
   snapPressed:= false;
   shiftPressed:= false;
 
-  //recursive calls
-  InFormMouseMove:= false;
-  InFormPaint := false;
-
   {$IFDEF LINUX}
   ComboBox_BrushSelect.Top := ComboBox_BrushSelect.Top - 2;
   ComboBox_BrushSelect.Font.Height := -10;
@@ -978,56 +918,10 @@ begin
   ComboBox_ArrowEnd.Font.Height := ComboBox_BrushSelect.Font.Height;
   {$ENDIF}
 
-  FLayout.OnPictureAreaChange := @LayoutPictureAreaChange;
   FInitialized := true;
   FirstPaint := true;
 end;
 
-function TFMain.CatchToolKeyDown(var AKey: Word): boolean;
-begin
-  if Assigned(ToolManager) then
-  begin
-    CatchPaintPicture:= true;
-    PaintPictureCatched := false;
-    try
-      result := ToolManager.ToolKeyDown(AKey) or PaintPictureCatched;
-    finally
-      CatchPaintPicture:= false;
-    end;
-  end else
-    result := false;
-end;
-
-function TFMain.CatchToolKeyUp(var AKey: Word): boolean;
-begin
-  if Assigned(ToolManager) then
-  begin
-    CatchPaintPicture:= true;
-    PaintPictureCatched := false;
-    try
-       result := ToolManager.ToolKeyUp(AKey) or PaintPictureCatched;
-    finally
-      CatchPaintPicture:= false;
-    end;
-  end else
-    result := false;
-end;
-
-function TFMain.CatchToolKeyPress(var AKey: TUTF8Char): boolean;
-begin
-  if Assigned(ToolManager) then
-  begin
-    CatchPaintPicture:= true;
-    PaintPictureCatched := false;
-    try
-      result := ToolManager.ToolKeyPress(AKey) or PaintPictureCatched;
-    finally
-      CatchPaintPicture:= false;
-    end;
-  end else
-    result := false;
-end;
-
 procedure TFMain.CreateMenuAndToolbar;
 begin
   CreateToolbarElements;
@@ -1079,8 +973,6 @@ begin
   FreeAndNil(Zoom);
   FreeAndNil(FOnlineUpdater);
 
-  FreeAndNil(FTablet);
-
   FreeAndNil(FBrowseSelections);
   FreeAndNil(FBrowseImages);
   FreeAndNil(FBrowseTextures);
@@ -1095,7 +987,6 @@ begin
   FreeAndNil(FSaveImage);
   FreeAndNil(FSaveSelection);
 
-  FreeAndNil(FImageView);
   FreeAndNil(FLayout);
 end;
 
@@ -1137,9 +1028,6 @@ begin
     ExportPictureDialog.FilterIndex:= 1;
   end;
 
-  FImageView := TImageView.Create(LazPaintInstance, Zoom,
-                {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture.Canvas{$ELSE}self.Canvas{$ENDIF});
-
   LazPaintInstance.EmbeddedResult := mrNone;
 
   Image.OnSelectedLayerIndexChanged:= @PictureSelectedLayerIndexChanged;
@@ -1316,146 +1204,6 @@ begin
   Scripting.RegisterScriptFunction('ShowColorDialog',@ScriptShowColorDialog,ARegister);
 end;
 
-procedure TFMain.FormMouseDown(Sender: TObject; Button: TMouseButton;
-  Shift: TShiftState; X, Y: Integer);
-begin
-  if not Assigned(FImageView) then exit;
-  ReleaseMouseButtons(Shift);
-  if not (Button in[mbLeft,mbRight,mbMiddle]) or not FImageView.PictureCoordsDefined then exit;
-  CanCompressOrUpdateStack := false;
-  if Assigned(LazPaintInstance) then LazPaintInstance.ExitColorEditor;
-  Image.OnImageChanged.DelayedStackUpdate := True;
-
-  if btnLeftDown or btnRightDown or btnMiddleDown then exit;
-
-  if Button = mbMiddle then
-  begin
-    btnMiddleDown:= true;
-    if not ToolManager.ToolSleeping and not (ssAlt in Shift) then ToolManager.ToolSleep;
-  end;
-
-  if FImageView.PictureCoordsDefined then
-  begin
-    if Button = mbLeft then
-      btnLeftDown := true else
-    if Button = mbRight then
-      btnRightDown := true;
-
-    if (
-        (ToolManager.GetCurrentToolType = ptHand) or
-        ((ToolManager.GetCurrentToolType = ptEditShape) and
-          Assigned(ToolManager.CurrentTool) and
-          (ToolManager.CurrentTool as TEditShapeTool).NothingSelected)
-       )  and
-       (ssShift in Shift) then
-      Image.SelectLayerContainingPixelAt(FImageView.FormToBitmap(X,Y).Round);
-
-    if ToolManager.ToolDown(FImageView.FormToBitmap(X,Y),
-        btnRightDown{$IFDEF DARWIN} or (ssCtrl in Shift){$ENDIF},
-        CurrentPressure) then
-        PaintPictureNow;
-    UpdateToolbar;
-  end;
-end;
-
-procedure TFMain.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
-  Y: Integer);
-var BmpPos: TPointF;
-    updateForVSCursor: boolean;
-
-//var tickstart:DWord;
-begin
-  //tickstart := GetTickCount;
-  if not Assigned(FImageView) then exit;
-
-  ReleaseMouseButtons(Shift);
-  UpdateSpecialKeys(Shift);
-  HidePenPreview;
-  HideFill;
-  if LazPaintInstance.TopMostHasFocus then
-  begin
-    if LazPaintInstance.TopMostOkToUnfocus then
-      SafeSetFocus(self)
-    else
-      exit;
-  end;
-  if (CurrentTool in[ptText,ptEditShape]) and TextSpinEditFocused then VectorialFill_Pen.SetFocus;
-  Image.CurrentState.LayeredBitmap.EditorFocused := true;
-
-  FormMouseMovePos := Point(X,Y);
-  if InFormMouseMove then exit;
-  InFormMouseMove := True;
-  if not FImageView.PictureCoordsDefined then
-    Application.ProcessMessages; //empty message stack
-  if not FImageView.PictureCoordsDefined then
-  begin
-    InFormMouseMove:= false;
-    exit;
-  end;
-
-  BmpPos := FImageView.FormToBitmap(FormMouseMovePos);
-  FCoordinatesCaption := IntToStr(round(BmpPos.X))+','+IntToStr(round(BmpPos.Y));
-  Inc(FCoordinatesCaptionCount);
-  if FCoordinatesCaptionCount > 8 then
-  begin
-    FCoordinatesCaptionCount := 0;
-    Label_Coordinates.caption := FCoordinatesCaption;
-    Label_Coordinates.Update;
-    UpdateStatusText;
-  end;
-  updateForVSCursor:= false;
-  if ToolManager.ToolMove(BmpPos,CurrentPressure) then
-  begin
-    FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea, self);
-  end else
-    updateForVSCursor := true;
-  UpdateToolbar;
-
-  if updateForVSCursor then
-    FImageView.UpdateCursor(X,Y, PictureCanvasOfs, FLayout.WorkArea,
-                            {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture{$ELSE}self{$ENDIF},
-                            Point(0,0), self);
-
-  if ToolManager.ToolSleeping and not spacePressed and not btnLeftDown and not btnRightDown
-    and not btnMiddleDown then
-    ToolManager.ToolWakeUp;
-
-  InFormMouseMove := False;
-  //Canvas.TextOut(FLayout.WorkArea.Left,FLayout.WorkArea.Top,inttostr(GetTickCount-tickstart)+'     ');
-end;
-
-procedure TFMain.FormMouseUp(Sender: TObject; Button: TMouseButton;
-  Shift: TShiftState; X, Y: Integer);
-var redraw: boolean;
-begin
-  if not Assigned(FImageView) then exit;
-
-  redraw := false;
-  if (btnLeftDown and (Button = mbLeft)) or (btnRightDown and (Button=mbRight))
-    or (btnMiddleDown and (Button = mbMiddle)) then
-  begin
-    if FImageView.PictureCoordsDefined then
-      redraw := ToolManager.ToolMove(FImageView.FormToBitmap(X,Y),CurrentPressure)
-      else redraw := false;
-    if ToolManager.ToolUp then redraw := true;
-    btnLeftDown := false;
-    btnRightDown := false;
-    btnMiddleDown:= false;
-  end;
-  if redraw then PaintPictureNow;
-  if FUpdateStackWhenIdle then
-  begin
-    UpdateStackOnTimer:= true;
-    FUpdateStackWhenIdle:= false;
-  end;
-  UpdateToolbar;
-  ReleaseMouseButtons(Shift);
-
-  if ToolManager.ToolSleeping and not spacePressed and not btnLeftDown and not btnRightDown
-   and not btnMiddleDown then
-    ToolManager.ToolWakeUp;
-end;
-
 function TFMain.ScriptFileOpen(AVars: TVariableSet): TScriptResult;
 var vFilename: TScriptVariableReference;
     topInfo: TTopMostInfo;
@@ -2030,7 +1778,7 @@ begin
     if Zoom.EditingZoom or EditingColors then exit;
     if not ((CurrentTool = ptText) and TextSpinEditFocused and (Key = VK_BACK)) and CatchToolKeyDown(Key) then
     begin
-      DelayedPaintPicture := True;
+      PaintPictureLater;
     end else
     if Key = VK_F6 then
     begin
@@ -2041,7 +1789,7 @@ begin
     begin
       spacePressed:= true;
       Key := 0;
-      if not ToolManager.ToolSleeping and not btnLeftDown and not btnRightDown then ToolManager.ToolSleep;
+      if not ToolManager.ToolSleeping and ([ssLeft,ssRight] * FLayout.MouseButtonState = []) then ToolManager.ToolSleep;
     end else
     if LazPaintInstance.ImageListWindowVisible then
       LazPaintInstance.ImageListWindowVisibleKeyDown(Key,Shift);
@@ -2450,22 +2198,18 @@ begin
   else if Key = VK_SHIFT then shiftPressed:= false;
   if CatchToolKeyUp(Key) then
   begin
-    DelayedPaintPicture := True;
+    PaintPictureLater;
   end else
   If Key = VK_SPACE then
   begin
     spacePressed:= false;
-    if ToolManager.ToolSleeping and not spacePressed and not btnRightDown and not btnLeftDown then
+    if ToolManager.ToolSleeping and not spacePressed and
+       ([ssLeft,ssRight] * FLayout.MouseButtonState = []) then
       ToolManager.ToolWakeUp;
     Key := 0;
   end;
 end;
 
-procedure TFMain.FormMouseEnter(Sender: TObject);
-begin
-  Image.PrepareForRendering;
-end;
-
 procedure TFMain.FormUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
 var selectedTool: TPaintToolType;
   toolProcessKey: boolean;
@@ -2480,7 +2224,7 @@ begin
     end;
     if toolProcessKey and CatchToolKeyPress(UTF8Key) then
     begin
-      DelayedPaintPicture := true;
+      PaintPictureLater;
       UpdateToolbar;
     end else
     if UTF8Key <> '' then
@@ -2522,11 +2266,6 @@ begin
   end;
 end;
 
-procedure TFMain.FormMouseLeave(Sender: TObject);
-begin
-  Cursor := crDefault;
-end;
-
 procedure TFMain.FormWindowStateChange(Sender: TObject);
 begin
   {$IFDEF LINUX}
@@ -2694,43 +2433,6 @@ begin
   LazPaintInstance.Donate;
 end;
 
-procedure TFMain.PaintBox_PictureMouseEnter(Sender: TObject);
-begin
-  FormMouseEnter(Sender);
-end;
-
-procedure TFMain.PaintBox_PictureMouseDown(Sender: TObject;
-  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
-begin
-  FormMouseDown(Sender,Button,Shift,X+PaintBox_Picture.Left,Y+PaintBox_Picture.Top);
-end;
-
-procedure TFMain.PaintBox_PictureMouseMove(Sender: TObject; Shift: TShiftState;
-  X, Y: Integer);
-begin
-  FormMouseMove(Sender,Shift,X+PaintBox_Picture.Left,Y+PaintBox_Picture.Top);
-end;
-
-procedure TFMain.PaintBox_PictureMouseUp(Sender: TObject; Button: TMouseButton;
-  Shift: TShiftState; X, Y: Integer);
-begin
-  FormMouseUp(Sender,Button,Shift,X+PaintBox_Picture.Left,Y+PaintBox_Picture.Top);
-end;
-
-procedure TFMain.PaintBox_PictureMouseWheel(Sender: TObject;
-  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
-  var Handled: Boolean);
-begin
-  FormMouseWheel(Sender,Shift,WheelDelta,Point(MousePos.X+PaintBox_Picture.Left,MousePos.Y+PaintBox_Picture.Top),Handled);
-end;
-
-procedure TFMain.PaintBox_PicturePaint(Sender: TObject);
-begin
-  {$IFDEF USEPAINTBOXPICTURE}
-    OnPaintHandler;
-  {$ENDIF}
-end;
-
 procedure TFMain.FormCloseQuery(Sender: TObject; var CanClose: boolean);
 var topmostInfo: TTopMostInfo;
 begin
@@ -2784,24 +2486,6 @@ begin
   LazPaintInstance.SaveMainWindowPosition;
 end;
 
-procedure TFMain.FormMouseWheel(Sender: TObject; Shift: TShiftState;
-  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
-begin
-  if not Assigned(FImageView) or not FImageView.PictureCoordsDefined then exit;
-  if ssAlt in Shift then
-  begin
-    if WheelDelta > 0 then IncreasePenSize
-    else if WheelDelta < 0 then DecreasePenSize;
-  end else
-  begin
-    Zoom.SetPosition(FImageView.FormToBitmap(MousePos.X,MousePos.Y), MousePos);
-    if WheelDelta > 0 then Zoom.ZoomIn(ssSnap in Shift) else
-    if WheelDelta < 0 then Zoom.ZoomOut(ssSnap in Shift);
-    Zoom.ClearPosition;
-  end;
-  Handled := True;
-end;
-
 procedure TFMain.HelpAboutExecute(Sender: TObject);
 begin
   LazPaintInstance.ShowAboutDlg;
@@ -2896,9 +2580,11 @@ begin
 end;
 
 procedure TFMain.TimerUpdateTimer(Sender: TObject);
-const SelectionPaintDelay = 100/(1000*60*60*24);
 begin
   TimerUpdate.Enabled := false;
+  if ToolManager.ToolSleeping and not spacePressed and
+     ([ssLeft,ssRight,ssMiddle] * FLayout.MouseButtonState = []) then
+    ToolManager.ToolWakeUp;
   EditUndo.Update;
   EditRedo.Update;
   UpdateStatusText;
@@ -2908,22 +2594,7 @@ begin
     Label_Coordinates.Update;
     FCoordinatesCaptionCount := 0;
   end;
-  if CanCompressOrUpdateStack and UpdateStackOnTimer then
-  begin
-    LazPaintInstance.NotifyStackChange;
-    UpdateStackOnTimer := false;
-  end else
-  begin
-    if CanCompressOrUpdateStack then image.CompressUndo;
-  end;
-  if DelayedPaintPicture or ToolManager.ToolUpdateNeeded or
-   (Assigned(FImageView) and not FImageView.ShowSelection and
-    (Now > FLastPaintDate+SelectionPaintDelay) ) then
-  begin
-    if ToolManager.ToolUpdateNeeded then ToolManager.ToolUpdate;
-    if Assigned(FImageView) then FImageView.ShowSelection := true;
-    PaintPictureNow;
-  end;
+  FLayout.CheckDelayedUpdate;
   TimerUpdate.Enabled := true;
 end;
 
@@ -3076,14 +2747,6 @@ begin
   params.Free;
 end;
 
-procedure TFMain.LayoutPictureAreaChange(ASender: TObject; ANewArea: TRect);
-begin
-   {$IFDEF USEPAINTBOXPICTURE}
-   PaintBox_Picture.SetBounds(ANewArea.Left,ANewArea.Top,ANewArea.Right-ANewArea.Left,ANewArea.Bottom-ANewArea.Top);
-   {$ENDIF}
-   if Assigned(FImageView) then FImageView.InvalidatePicture(True, ANewArea, Point(0,0), self);
-end;
-
 procedure TFMain.ToggleGridVisible;
 begin
   LazPaintInstance.GridVisible := not LazPaintInstance.GridVisible;
@@ -3301,8 +2964,7 @@ begin
         ToolManager.ToolMove(texMapBounds.Right-0.5, texMapBounds.Bottom-0.5, 1);
         ToolManager.ToolUp;
       end;
-      if Assigned(FImageView) then
-        FImageView.FillSelectionHighlight := ToolManager.DisplayFilledSelection and not FShowSelectionNormal;
+      FLayout.FillSelectionHighlight := ToolManager.DisplayFilledSelection and not FShowSelectionNormal;
     except
       on ex:Exception do
       begin
@@ -3382,7 +3044,6 @@ begin
 
   if needUpdate then
   begin
-    FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea, self);
     PaintPictureNow;
     UpdateToolbar;
   end;
@@ -3460,7 +3121,6 @@ begin
 
   if needUpdate then
   begin
-    FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea, self);
     PaintPictureNow;
     UpdateToolbar;
   end;
@@ -3508,7 +3168,6 @@ begin
     end;
     if needUpdate then
     begin
-      FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea, self);
       PaintPictureNow;
       UpdateToolbar;
     end;
@@ -4007,30 +3666,6 @@ begin
   end;
 end;
 
-procedure TFMain.ReleaseMouseButtons(Shift: TShiftState);
-begin
-  if not (ssLeft in Shift) and btnLeftDown then
-  begin
-    btnLeftDown := false;
-    if ToolManager.ToolUp then PaintPictureNow;
-  end;
-  if not (ssRight in Shift) and btnRightDown then
-  begin
-    btnRightDown := false;
-    if ToolManager.ToolUp then PaintPictureNow;
-  end;
-  if not (ssMiddle in Shift) and btnMiddleDown then
-  begin
-    btnMiddleDown := false;
-    if ToolManager.ToolUp then PaintPictureNow;
-  end;
-  if not btnLeftDown and not btnRightDown then
-  begin
-    CanCompressOrUpdateStack := true;
-    Image.OnImageChanged.DelayedStackUpdate := False;
-  end;
-end;
-
 procedure TFMain.UpdateSpecialKeys(Shift: TShiftState);
   procedure UpdateKey(AShift: TShiftStateEnum; ACode: Word; var APressed: boolean);
   begin
@@ -4194,9 +3829,8 @@ end;
 
 procedure TFMain.ShowNoPicture;
 begin
-  InShowNoPicture := true;
-  PaintPictureNow;
-  InShowNoPicture:= false;
+  if Assigned(FLayout) then
+    FLayout.ShowNoPicture;
 end;
 
 procedure TFMain.UpdateWindowCaption;
@@ -4430,36 +4064,8 @@ end;
 
 {****************************** Picture ************************}
 
-procedure TFMain.OnPaintHandler;
-var
-  ac: TWinControl;
-begin
-  if FirstPaint then
-  begin
-    LoadToolwindow := True;
-    TimerLoadToolWin.Enabled := true;
-    FirstPaint := false;
-  end;
-  if InFormPaint then exit;
-  InFormPaint := true;
-
-  if Assigned(FImageView) then FImageView.DoPaint(PictureCanvasOfs, FLayout.WorkArea, InShowNoPicture);
-  DelayedPaintPicture:= false;
-
-  ac := ActiveControl;
-  if ac is TBCTrackbarUpdown then
-    TBCTrackbarUpdown(ac).DelayTimer;
-
-  InFormPaint := false;
-  FLastPaintDate := Now;
-end;
-
 procedure TFMain.OnImageChangedHandler(AEvent: TLazPaintImageObservationEvent);
 begin
-  if CatchPaintPicture then
-    PaintPictureCatched := true
-    else InvalidatePicture(false);
-
   if (image.Width <> FLastWidth) or (image.Height <> FLastHeight)
    or (image.BPP <> FLastBPP) or (image.FrameIndex <> FLastFrameIndex) then
   begin
@@ -4474,7 +4080,6 @@ begin
     ChooseTool(ptHand);
     MessagePopup(rsToolOnInvisibleLayer,5000);
   end;
-  if AEvent.DelayedStackUpdate then FUpdateStackWhenIdle := true;
 end;
 
 procedure TFMain.OnImageRenderChanged(Sender: TObject);
@@ -4486,37 +4091,78 @@ procedure TFMain.UpdateEditPicture(ADelayed: boolean = false);
 begin
   if ToolManager.ToolUpdate then
   begin
-    if ADelayed then DelayedPaintPicture := True
+    if ADelayed then PaintPictureLater
     else
       PaintPictureNow;
   end;
 end;
 
-procedure TFMain.OnZoomChanged(sender: TZoom; ANewZoom: single);
+procedure TFMain.PaintPictureNow;
+begin
+  if not visible then exit;
+  if Assigned(LazPaintInstance) then LazPaintInstance.UpdateStackOnTimer := true;
+  if Assigned(FLayout) then FLayout.PaintPictureNow;
+end;
+
+procedure TFMain.PaintPictureLater;
+begin
+  if Assigned(FLayout) then FLayout.DelayedPaintPicture := True;
+end;
+
+procedure TFMain.NotifyImagePaint;
+begin
+  if Assigned(FLayout) then
+    FLayout.DelayedPaintPicture:= false;
+end;
+
+procedure TFMain.PictureMouseBefore(Sender: TObject; AShift: TShiftState);
+begin
+  UpdateSpecialKeys(AShift);
+end;
+
+procedure TFMain.PictureMouseMove(Sender: TObject; APosition: TPointF);
 begin
-  if Assigned(FImageView) then
+  HidePenPreview;
+  HideFill;
+
+  FCoordinatesCaption := IntToStr(round(APosition.X))+','+IntToStr(round(APosition.Y));
+  Inc(FCoordinatesCaptionCount);
+  if FCoordinatesCaptionCount > 8 then
   begin
-    if not Image.SelectionMaskEmpty then
-      FImageView.ShowSelection := false;
-    FImageView.OnZoomChanged(sender, ANewZoom, FLayout.WorkArea);
+    FCoordinatesCaptionCount := 0;
+    Label_Coordinates.caption := FCoordinatesCaption;
+    Label_Coordinates.Update;
+    UpdateStatusText;
   end;
-  UpdateToolbar;
-  PaintPictureNow;
+
+  if LazPaintInstance.TopMostHasFocus then
+  begin
+    if LazPaintInstance.TopMostOkToUnfocus then
+      SafeSetFocus(self)
+    else
+      exit;
+  end;
+  if (CurrentTool in[ptText,ptEditShape]) and TextSpinEditFocused then VectorialFill_Pen.SetFocus;
 end;
 
-procedure TFMain.PaintPictureNow;
+procedure TFMain.PictureOnPaint(Sender: TObject);
+var
+  ac: TWinControl;
 begin
-  if not visible then exit;
-  UpdateStackOnTimer := true;
-  Image.OnImageChanged.NotifyObservers;
-  {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture{$ELSE}self{$ENDIF}.Update;
+  if FirstPaint then
+  begin
+    LoadToolwindow := True;
+    TimerLoadToolWin.Enabled := true;
+    FirstPaint := false;
+  end;
+  ac := ActiveControl;
+  if ac is TBCTrackbarUpdown then
+    TBCTrackbarUpdown(ac).DelayTimer;
 end;
 
-procedure TFMain.FormPaint(Sender: TObject);
+procedure TFMain.PictureToolbarUpdate(Sender: TObject);
 begin
-  {$IFNDEF USEPAINTBOXPICTURE}
-  OnPaintHandler;
-  {$ENDIF}
+  UpdateToolbar;
 end;
 
 procedure TFMain.PictureSelectedLayerIndexChanged(sender: TLazPaintImage);
@@ -4538,8 +4184,8 @@ end;
 procedure TFMain.SetShowSelectionNormal(const AValue: boolean);
 begin
   FShowSelectionNormal := AValue;
-  if Assigned(FImageView) then
-    FImageView.FillSelectionHighlight := ToolManager.DisplayFilledSelection and not FShowSelectionNormal;
+  if Assigned(FLayout) then
+    FLayout.FillSelectionHighlight := ToolManager.DisplayFilledSelection and not FShowSelectionNormal;
 end;
 
 procedure TFMain.WMEraseBkgnd(var Message: TLMEraseBkgnd);
@@ -4673,8 +4319,8 @@ end;
 
 procedure TFMain.InvalidatePicture(AInvalidateAll: boolean = true);
 begin
-  if Assigned(FImageView) and Assigned(FLayout) then
-    FImageView.InvalidatePicture(AInvalidateAll, FLayout.WorkArea, Point(0,0), self);
+  if Assigned(FLayout) then
+    FLayout.InvalidatePicture(AInvalidateAll);
 end;
 
 function TFMain.GetUseImageBrowser: boolean;
@@ -4695,15 +4341,8 @@ end;
 
 procedure TFMain.SetUpdatingPopup(AValue: boolean);
 begin
-  FImageView.UpdatingPopup := AValue;
-end;
-
-function TFMain.GetCurrentPressure: single;
-begin
-  if Assigned(FTablet) and FTablet.Present and FTablet.Entering and (FTablet.Max > 0) then
-    result := FTablet.Pressure/FTablet.Max
-  else
-    result := 1;
+  if Assigned(FLayout) then
+    FLayout.UpdatingPopup := AValue;
 end;
 
 function TFMain.GetDarkTheme: boolean;
@@ -4719,7 +4358,9 @@ end;
 
 function TFMain.GetUpdatingPopup: boolean;
 begin
-  result := FImageView.UpdatingPopup;
+  if Assigned(FLayout) then
+    result := FLayout.UpdatingPopup
+    else result := false;
 end;
 
 function TFMain.GetScriptContext: TScriptContext;
@@ -4741,15 +4382,6 @@ begin
   end;
 end;
 
-function TFMain.PictureCanvasOfs: TPoint;
-begin
-  {$IFDEF USEPAINTBOXPICTURE}
-  result := Point(-PaintBox_Picture.Left,-PaintBox_Picture.Top);
-  {$ELSE}
-  result := Point(0,0);
-  {$ENDIF}
-end;
-
 {$R *.lfm}
 
 end.

+ 7 - 0
lazpaint/lazpainttype.pas

@@ -181,6 +181,9 @@ type
     procedure SetBlackAndWhite(AValue: boolean); virtual;
     function GetZoomFactor: single; virtual;
 
+    function GetUpdateStackOnTimer: boolean; virtual; abstract;
+    procedure SetUpdateStackOnTimer(AValue: boolean); virtual; abstract;
+
     function GetChooseColorHeight: integer; virtual; abstract;
     function GetChooseColorWidth: integer; virtual; abstract;
     procedure SetChooseColorHeight(AValue: integer); virtual; abstract;
@@ -241,6 +244,7 @@ type
     procedure CancelRestart; virtual; abstract;
     procedure NotifyImageChange(RepaintNow: boolean; ARect: TRect); virtual; abstract;
     procedure NotifyImageChangeCompletely(RepaintNow: boolean); virtual; abstract;
+    procedure NotifyImagePaint; virtual; abstract;
     procedure NotifyStackChange; virtual; abstract;
     function TryOpenFileUTF8(filename: string; skipDialogIfSingleImage: boolean = false): boolean; virtual; abstract;
     function ExecuteFilter(filter: TPictureFilter; skipDialog: boolean = false): TScriptResult; virtual; abstract;
@@ -294,6 +298,8 @@ type
     property BlackAndWhite: boolean read FBlackAndWhite write SetBlackAndWhite;
 
     procedure ScrollLayerStackOnItem(AIndex: integer; ADelayedUpdate: boolean = true); virtual; abstract;
+    procedure InvalidateLayerStack; virtual; abstract;
+    procedure UpdateLayerStackOnTimer; virtual; abstract;
     function MakeNewBitmapReplacement(AWidth, AHeight: integer; AColor: TBGRAPixel): TBGRABitmap; virtual; abstract;
     procedure ChooseTool(Tool : TPaintToolType); virtual; abstract;
     function GetOnlineUpdater: TLazPaintCustomOnlineUpdater; virtual;
@@ -340,6 +346,7 @@ type
     property Fullscreen: boolean read GetFullscreen write SetFullscreen;
     property RestartQuery: boolean read FRestartQuery;
     property DarkTheme: boolean read GetDarkTheme write SetDarkTheme;
+    property UpdateStackOnTimer: boolean read GetUpdateStackOnTimer write SetUpdateStackOnTimer;
 
     property Icons[ASize: integer]: TImageList read GetIcons;
   end;

+ 23 - 28
lazpaint/maintoolbar.inc

@@ -668,6 +668,27 @@ begin
   FInitialized := oldInit;
 end;
 
+function TFMain.CatchToolKeyDown(var AKey: Word): boolean;
+begin
+  if Assigned(FLayout) then
+    result := FLayout.CatchToolKeyDown(AKey)
+    else result := false;
+end;
+
+function TFMain.CatchToolKeyUp(var AKey: Word): boolean;
+begin
+  if Assigned(FLayout) then
+    result := FLayout.CatchToolKeyUp(AKey)
+    else result := false;
+end;
+
+function TFMain.CatchToolKeyPress(var AKey: TUTF8Char): boolean;
+begin
+  if Assigned(FLayout) then
+    result := FLayout.CatchToolKeyPress(AKey)
+    else result := false;
+end;
+
 procedure TFMain.SpinEdit_ShapeAltitudeChange(Sender: TObject; AByUser: boolean);
 begin
   if SpinEdit_ShapeAltitude.Value < 6 then
@@ -1506,39 +1527,13 @@ begin
   if SpinEdit_PenWidth.Value < MinPenWidth*PenWidthFactor then SpinEdit_PenWidth.Value := MinPenWidth*PenWidthFactor;
 end;
 
-procedure TFMain.IncreasePenSize;
-begin
-  SpinEdit_PenWidth.Value := max(SpinEdit_PenWidth.Value+PenSizeDelta(1),MinPenWidth*PenWidthFactor);
-  UpdatePenWidthFromSpinEdit;
-end;
-
-procedure TFMain.DecreasePenSize;
-begin
-  SpinEdit_PenWidth.Value := max(SpinEdit_PenWidth.Value-PenSizeDelta(-1),MinPenWidth*PenWidthFactor);
-  UpdatePenWidthFromSpinEdit;
-end;
-
-function TFMain.PenSizeDelta(direction: integer): integer;
-var v: integer;
-begin
-  v := SpinEdit_PenWidth.Value;
-  if direction < 0 then dec(v);
-  if v < 100 then result := 10 else
-  if v < 200 then result := 20 else
-  if v < 500 then result := 50 else
-  if v < 1000 then result := 100 else
-  if v < 2000 then result := 200 else
-  if v < 5000 then result := 500 else
-    result := 1000;
-end;
-
 procedure TFMain.PaintBox_PenPreviewMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 var t: integer;
 begin
   t := ImageList16.Width;
-  if (X < t) and (Y < t) then DecreasePenSize else
-  if (X >= PaintBox_PenPreview.Width-t) and (Y < t) then IncreasePenSize;
+  if (X < t) and (Y < t) then ToolManager.StepPenSize(True) else
+  if (X >= PaintBox_PenPreview.Width-t) and (Y < t) then ToolManager.StepPenSize(False);
   ShowPenPreview(False);
 end;
 

+ 21 - 0
lazpaint/tools/utool.pas

@@ -460,6 +460,7 @@ type
     function ApplyPressure(AOpacity: byte): byte;
     procedure SetPressure(APressure: single);
     function GetPressureB: Byte;
+    procedure StepPenSize(ADecrease: boolean);
 
     function GetCurrentToolType: TPaintToolType;
     function SetCurrentToolType(tool: TPaintToolType): boolean;
@@ -3118,6 +3119,26 @@ begin
   result := round(FToolPressure*255);
 end;
 
+procedure TToolManager.StepPenSize(ADecrease: boolean);
+  function SizeDelta: single;
+  var v: single;
+  begin
+    v := PenWidth;
+    if ADecrease then v := v - 0.1;
+    if v < 10 then result := 1 else
+    if v < 20 then result := 2 else
+    if v < 50 then result := 5 else
+    if v < 100 then result := 10 else
+    if v < 200 then result := 20 else
+    if v < 500 then result := 50 else
+      result := 100;
+  end;
+begin
+  if ADecrease then
+    PenWidth := PenWidth - SizeDelta
+    else PenWidth := PenWidth + SizeDelta;
+end;
+
 procedure TToolManager.InternalSetCurrentToolType(tool: TPaintToolType);
 begin
   if (tool <> FCurrentToolType) or (FCurrentTool=nil) then

+ 433 - 85
lazpaint/uimageview.pas

@@ -8,15 +8,20 @@ interface
 
 uses
   Classes, SysUtils, USelectionHighlight, BGRABitmap, BGRABitmapTypes,
-  LazPaintType, UImage, UZoom, Graphics, Controls;
+  LazPaintType, UImage, UZoom, Graphics, Controls, LCLType, UImageObservation,
+  laztablet;
 
 type
+  TPictureMouseMoveEvent = procedure(ASender: TObject; APosition: TPointF) of object;
+  TPictureMouseBeforeEvent = procedure(ASender: TObject; AShift: TShiftState) of object;
 
   { TImageView }
 
   TImageView = class
+  private
+    function GetMouseButtonState: TShiftState;
   protected
-    FVirtualScreen : TBGRABitmap;
+    FVirtualScreen: TBGRABitmap;
     FUpdatingPopup: boolean;
     FPenCursorVisible: boolean;
     FPenCursorPos,FPenCursorPosBefore: TVSCursorPosition;
@@ -35,33 +40,65 @@ type
        imageWidth,imageHeight: integer;
     end;
     FZoom: TZoom;
-    FPictureCanvas: TCanvas;
+    FPaintBox: TGraphicControl;
+    FormMouseMovePos: TPoint;
+    InFormMouseMove: boolean;
+    InFormPaint: boolean;
+    FOnPaint: TNotifyEvent;
+    FOnToolbarUpdate: TNotifyEvent;
+    FOnMouseMove: TPictureMouseMoveEvent;
+    FOnMouseBefore: TPictureMouseBeforeEvent;
+    btnLeftDown, btnRightDown, btnMiddleDown: boolean;
+    FLastPaintDate: TDateTime;
+    FUpdateStackWhenIdle: boolean;
+    FCatchPaintPicture, FPaintPictureCatched: boolean;
+    InShowNoPicture: boolean;
+    FCanCompressOrUpdateStack: boolean;
+    FTablet: TLazTablet;
+    FImagePos: TPointF;
     function GetImage: TLazPaintImage;
+    function GetPictureCanvas: TCanvas;
+    function GetWorkArea: TRect;
+    procedure PaintBoxMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+    procedure PaintBoxMouseEnter(Sender: TObject);
+    procedure PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
+    procedure PaintBoxMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+    procedure PaintBoxMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+    procedure PaintBoxPaint(Sender: TObject);
+    procedure ImageChanged(AEvent: TLazPaintImageObservationEvent);
     function GetRenderUpdateRectVS(AIncludeCurrentToolEditor: boolean): TRect;
     function GetFillSelectionHighlight: boolean;
     function GetPenCursorPosition: TVSCursorPosition;
     function GetWorkspaceColor: TColor;
-    procedure PaintPictureImplementation(ACanvasOfs: TPoint; AWorkArea: TRect; AVSPart: TRect);
-    procedure PaintVirtualScreenImplementation(ACanvasOfs: TPoint; AWorkArea: TRect; AVSPart: TRect);
-    procedure PaintBlueAreaImplementation(ACanvasOfs: TPoint; AWorkArea: TRect);
-    procedure PaintBlueAreaOnly(ACanvasOfs: TPoint; AWorkArea: TRect);
+    procedure PaintPictureImplementation(AWorkArea: TRect; AVSPart: TRect);
+    procedure PaintVirtualScreenImplementation(AWorkArea: TRect; AVSPart: TRect);
+    procedure PaintBlueAreaImplementation(AWorkArea: TRect);
+    procedure PaintBlueAreaOnly(AWorkArea: TRect);
     procedure ComputePictureParams(AWorkArea: TRect);
     procedure SetFillSelectionHighlight(AValue: boolean);
     procedure SetShowSelection(AValue: boolean);
     procedure PictureSelectionChanged({%H-}sender: TLazPaintImage; const ARect: TRect);
     procedure ToolManagerRenderChanged(Sender: TObject);
-    procedure PaintVirtualScreenCursor({%H-}ACanvasOfs: TPoint; {%H-}AWorkArea: TRect; {%H-}AWinControlOfs: TPoint; {%H-}AWinControl: TWinControl);
+    procedure PaintVirtualScreenCursor({%H-}AWorkArea: TRect);
     function GetRectToInvalidate(AInvalidateAll: boolean; AWorkArea: TRect): TRect;
     function GetPictureCoordsDefined: boolean;
+    procedure DoInvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect);
+    procedure DoPaint(AWorkArea: TRect; AShowNoPicture: boolean);
+    procedure DoUpdatePicture(AWorkArea: TRect);
+    procedure ReleaseMouseButtons(Shift: TShiftState);
+    function GetCurrentPressure: single;
   public
-    constructor Create(AInstance: TLazPaintCustomInstance; AZoom: TZoom; ACanvas: TCanvas);
+    constructor Create(AInstance: TLazPaintCustomInstance; AZoom: TZoom; APaintBox: TGraphicControl);
     destructor Destroy; override;
-    procedure DoPaint(ACanvasOfs: TPoint; AWorkArea: TRect; AShowNoPicture: boolean);
-    procedure InvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect; AControlOfs: TPoint; AWinControl: TWinControl);
-    procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single; AWorkArea: TRect);
-    procedure UpdateCursor(X,Y: integer; ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl;
-                          AWinControlOfs: TPoint; AWinControl: TWinControl);
-    procedure UpdatePicture({%H-}ACanvasOfs: TPoint; AWorkArea: TRect; {%H-}AWinControl: TWinControl);
+    function CatchToolKeyDown(var AKey: Word): boolean;
+    function CatchToolKeyPress(var AKey: TUTF8Char): boolean;
+    function CatchToolKeyUp(var AKey: Word): boolean;
+    procedure SetBounds(ALeft, ATop, AWidth, AHeight: integer);
+    procedure UpdatePicture;
+    procedure ShowNoPicture;
+    procedure InvalidatePicture(AInvalidateAll: boolean);
+    procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single);
+    procedure UpdateCursor(X,Y: integer);
     function BitmapToForm(pt: TPointF): TPointF;
     function BitmapToForm(X, Y: Single): TPointF;
     function BitmapToVirtualScreen(ptF: TPointF): TPointF;
@@ -71,17 +108,40 @@ type
     property Image: TLazPaintImage read GetImage;
     property Zoom: TZoom read FZoom;
     property LazPaintInstance: TLazPaintCustomInstance read FInstance;
-    property PictureCanvas: TCanvas read FPictureCanvas;
+    property PictureCanvas: TCanvas read GetPictureCanvas;
     property FillSelectionHighlight: boolean read GetFillSelectionHighlight write SetFillSelectionHighlight;
     property ShowSelection: boolean read FShowSelection write SetShowSelection;
     property WorkspaceColor: TColor read GetWorkspaceColor;
+    property WorkArea: TRect read GetWorkArea;
     property PictureCoordsDefined: boolean read GetPictureCoordsDefined;
     property UpdatingPopup: boolean read FUpdatingPopup write FUpdatingPopup;
+    property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
+    property OnMouseMove: TPictureMouseMoveEvent read FOnMouseMove write FOnMouseMove;
+    property OnMouseBefore: TPictureMouseBeforeEvent read FOnMouseBefore write FOnMouseBefore;
+    property OnToolbarUpdate: TNotifyEvent read FOnToolbarUpdate write FOnToolbarUpdate;
+    property LastPaintDate: TDateTime read FLastPaintDate;
+    property MouseButtonState: TShiftState read GetMouseButtonState;
+    property CanCompressOrUpdateStack: boolean read FCanCompressOrUpdateStack;
   end;
 
 implementation
 
-uses BGRATransform, LCLIntf, Types, ugraph, math, UTool, BGRAThumbnail, LCScaleDPI;
+uses BGRATransform, LCLIntf, Types, ugraph, math, UTool, BGRAThumbnail, LCScaleDPI, Forms,
+  UToolVectorial, ExtCtrls;
+
+procedure InvalidateControlRect(AControl: TControl; AArea: TRect);
+var
+  h: HWND;
+begin
+  if AControl is TWinControl then
+    h := TWinControl(AControl).Handle
+  else
+  begin
+    AArea.Offset(AControl.Left, AControl.Top);
+    h := AControl.Parent.Handle;
+  end;
+  InvalidateRect(h, @AArea, False);
+end;
 
 function TImageView.GetFillSelectionHighlight: boolean;
 begin
@@ -119,12 +179,185 @@ begin
                             FVirtualScreen.Width, FVirtualScreen.Height));
 end;
 
+procedure TImageView.PaintBoxMouseEnter(Sender: TObject);
+begin
+  Image.PrepareForRendering;
+end;
+
+procedure TImageView.PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
+  Y: Integer);
+var
+  updateForVSCursor: boolean;
+begin
+  if Assigned(FOnMouseBefore) then
+    FOnMouseBefore(self, Shift);
+  ReleaseMouseButtons(Shift);
+  Image.CurrentState.LayeredBitmap.EditorFocused := true;
+
+  FormMouseMovePos := Point(X,Y);
+  if InFormMouseMove then exit;
+  InFormMouseMove := True;
+  if not PictureCoordsDefined then
+    Application.ProcessMessages; //empty message stack
+  if not PictureCoordsDefined then
+  begin
+    InFormMouseMove:= false;
+    exit;
+  end;
+
+  FImagePos := FormToBitmap(FormMouseMovePos);
+  if Assigned(FOnMouseMove) then
+    FOnMouseMove(self, FImagePos);
+
+  updateForVSCursor:= false;
+  if LazPaintInstance.ToolManager.ToolMove(FImagePos, GetCurrentPressure) then
+    UpdatePicture
+  else
+    updateForVSCursor := true;
+  if Assigned(FOnToolbarUpdate) then
+    FOnToolbarUpdate(self);
+
+  if updateForVSCursor then
+    UpdateCursor(X,Y);
+
+  InFormMouseMove := False;
+end;
+
+procedure TImageView.PaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
+  Shift: TShiftState; X, Y: Integer);
+var redraw: boolean;
+begin
+  redraw := false;
+  if (btnLeftDown and (Button = mbLeft)) or (btnRightDown and (Button=mbRight))
+    or (btnMiddleDown and (Button = mbMiddle)) then
+  begin
+    if PictureCoordsDefined then
+      redraw := LazPaintInstance.ToolManager.ToolMove(FormToBitmap(X,Y), GetCurrentPressure)
+      else redraw := false;
+    if LazPaintInstance.ToolManager.ToolUp then redraw := true;
+    btnLeftDown := false;
+    btnRightDown := false;
+    btnMiddleDown:= false;
+  end;
+  if redraw then UpdatePicture;
+  if FUpdateStackWhenIdle then
+  begin
+    LazPaintInstance.UpdateLayerStackOnTimer;
+    FUpdateStackWhenIdle:= false;
+  end;
+  if Assigned(FOnToolbarUpdate) then
+    FOnToolbarUpdate(self);
+  ReleaseMouseButtons(Shift);
+end;
+
+procedure TImageView.PaintBoxMouseWheel(Sender: TObject; Shift: TShiftState;
+  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+begin
+  if not PictureCoordsDefined then exit;
+  if ssAlt in Shift then
+  begin
+    if WheelDelta > 0 then LazPaintInstance.ToolManager.StepPenSize(false)
+    else if WheelDelta < 0 then LazPaintInstance.ToolManager.StepPenSize(true);
+  end else
+  begin
+    Zoom.SetPosition(FormToBitmap(MousePos.X,MousePos.Y), MousePos);
+    if WheelDelta > 0 then Zoom.ZoomIn(ssSnap in Shift) else
+    if WheelDelta < 0 then Zoom.ZoomOut(ssSnap in Shift);
+    Zoom.ClearPosition;
+  end;
+  Handled := True;
+end;
+
+procedure TImageView.PaintBoxMouseDown(Sender: TObject; Button: TMouseButton;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  ReleaseMouseButtons(Shift);
+  if not (Button in[mbLeft,mbRight,mbMiddle]) or not PictureCoordsDefined then exit;
+  FCanCompressOrUpdateStack := false;
+  if Assigned(LazPaintInstance) then LazPaintInstance.ExitColorEditor;
+  Image.OnImageChanged.DelayedStackUpdate := True;
+
+  if btnLeftDown or btnRightDown or btnMiddleDown then exit;
+
+  if Button = mbMiddle then
+  begin
+    btnMiddleDown:= true;
+    if not LazPaintInstance.ToolManager.ToolSleeping and not (ssAlt in Shift) then LazPaintInstance.ToolManager.ToolSleep;
+  end;
+
+  if PictureCoordsDefined then
+  begin
+    if Button = mbLeft then
+      btnLeftDown := true else
+    if Button = mbRight then
+      btnRightDown := true;
+
+    with LazPaintInstance.ToolManager do
+    begin
+      if (
+          (GetCurrentToolType = ptHand) or
+          ((GetCurrentToolType = ptEditShape) and
+            Assigned(CurrentTool) and
+            (CurrentTool as TEditShapeTool).NothingSelected)
+         )  and
+         (ssShift in Shift) then
+        Image.SelectLayerContainingPixelAt(FormToBitmap(X,Y).Round);
+
+      if ToolDown(FormToBitmap(X,Y),
+          btnRightDown{$IFDEF DARWIN} or (ssCtrl in Shift){$ENDIF},
+          GetCurrentPressure) then
+          UpdatePicture;
+    end;
+
+    if Assigned(FOnToolbarUpdate) then
+      FOnToolbarUpdate(self);
+  end;
+end;
+
+function TImageView.GetPictureCanvas: TCanvas;
+begin
+  result := FPaintBox.Canvas;
+end;
+
+procedure TImageView.PaintBoxPaint(Sender: TObject);
+begin
+  if InFormPaint then exit;
+  InFormPaint := true;
+
+  DoPaint(WorkArea, InShowNoPicture);
+  LazPaintInstance.NotifyImagePaint;
+
+  InFormPaint := false;
+  FLastPaintDate := Now;
+end;
+
+function TImageView.GetWorkArea: TRect;
+begin
+  result := rect(0, 0, FPaintBox.Width, FPaintBox.Height);
+end;
+
+procedure TImageView.ImageChanged(AEvent: TLazPaintImageObservationEvent);
+begin
+  if AEvent.DelayedStackUpdate then FUpdateStackWhenIdle := true;
+  if FCatchPaintPicture then
+    FPaintPictureCatched := true
+    else InvalidatePicture(false);
+end;
+
+function TImageView.GetMouseButtonState: TShiftState;
+begin
+  result := [];
+  if btnLeftDown then include(result, ssLeft);
+  if btnMiddleDown then include(result, ssMiddle);
+  if btnRightDown then include(result, ssRight);
+end;
+
 function TImageView.GetImage: TLazPaintImage;
 begin
   result := FInstance.Image;
 end;
 
-procedure TImageView.PaintPictureImplementation(ACanvasOfs: TPoint; AWorkArea: TRect; AVSPart: TRect);
+procedure TImageView.PaintPictureImplementation(AWorkArea: TRect; AVSPart: TRect);
 var
   renderRect: TRect;
   picParamWereDefined: boolean;
@@ -204,23 +437,24 @@ begin
     Image.RenderMayChange(LazPaintInstance.ToolManager.RenderTool(FVirtualScreen), false, false);
   end;
 
-  PaintVirtualScreenImplementation(ACanvasOfs, AWorkArea, AVSPart);
+  PaintVirtualScreenImplementation(AWorkArea, AVSPart);
   Image.VisibleArea := TRectF.Intersect(rectF(FormToBitmap(AWorkArea.Left, AWorkArea.Top),
                                               FormToBitmap(AWorkArea.Right, AWorkArea.Bottom)),
                           rectF(-0.5,-0.5,Image.Width-0.5,Image.Height-0.5));
 end;
 
-procedure TImageView.PaintVirtualScreenImplementation(ACanvasOfs: TPoint; AWorkArea: TRect; AVSPart: TRect);
+procedure TImageView.PaintVirtualScreenImplementation(AWorkArea: TRect; AVSPart: TRect);
 var cursorBack: TBGRABitmap;
     DestCanvas: TCanvas;
-    DrawOfs: TPoint;
     cursorContourF: array of TPointF;
     rectBack: TRect;
     cursorPos: TVSCursorPosition;
 
   procedure DrawPart;
   begin
-    FVirtualScreen.DrawPart(AVSPart, DestCanvas, DrawOfs.X+AVSPart.Left, DrawOfs.Y+AVSPart.Top, True);
+    FVirtualScreen.DrawPart(AVSPart, DestCanvas,
+      FLastPictureParameters.virtualScreenArea.Left+AVSPart.Left,
+      FLastPictureParameters.virtualScreenArea.Top+AVSPart.Top, True);
   end;
 
 begin
@@ -229,9 +463,6 @@ begin
   if AVSPart.IsEmpty then exit;
 
   DestCanvas := PictureCanvas;
-  DrawOfs := ACanvasOfs;
-  Inc(DrawOfs.X, FLastPictureParameters.virtualScreenArea.Left);
-  inc(DrawOfs.Y, FLastPictureParameters.virtualScreenArea.Top);
 
   cursorPos := FPenCursorPos;
   if FPenCursorVisible and not IsRectEmpty(cursorPos.bounds) then
@@ -263,88 +494,177 @@ begin
     Zoom.MaxFactor := min(32,max(1,min((right-left)/8,(bottom-top)/8)));
 end;
 
-procedure TImageView.PaintBlueAreaImplementation(ACanvasOfs: TPoint; AWorkArea: TRect);
+procedure TImageView.PaintBlueAreaImplementation(AWorkArea: TRect);
 var
-  DrawOfs: TPoint;
-  workArea, scaledArea: TRect;
+  lastWorkArea, scaledArea: TRect;
 begin
   if FLastPictureParameters.defined then
   begin
-    workArea := FLastPictureParameters.workArea;
-    if (workArea.Right <= workArea.Left) or (workArea.Bottom <= workArea.Top) then exit;
+    lastWorkArea := FLastPictureParameters.WorkArea;
+    if (lastWorkArea.Right <= lastWorkArea.Left) or (lastWorkArea.Bottom <= lastWorkArea.Top) then exit;
     scaledArea := FLastPictureParameters.scaledArea;
-    IntersectRect(scaledArea, scaledArea,workArea);
+    IntersectRect(scaledArea, scaledArea,lastWorkArea);
     with PictureCanvas do
     begin
       Brush.Color := WorkspaceColor;
-      DrawOfs := ACanvasOfs;
-      if scaledArea.Left > workArea.Left then
-        FillRect(workArea.Left+DrawOfs.X,scaledArea.Top+DrawOfs.Y,scaledArea.Left+DrawOfs.X,scaledArea.Bottom+DrawOfs.Y);
-      if scaledArea.Top > workArea.Top then
-        FillRect(workArea.Left+DrawOfs.X,workArea.Top+DrawOfs.Y,workArea.Right+DrawOfs.X,scaledArea.Top+DrawOfs.Y);
-      if scaledArea.Right < workArea.Right then
-        FillRect(scaledArea.Right+DrawOfs.X,scaledArea.Top+DrawOfs.Y,workArea.Right+DrawOfs.X,scaledArea.Bottom+DrawOfs.Y);
-      if scaledArea.Bottom < workArea.Bottom then
-        FillRect(workArea.Left+DrawOfs.X,scaledArea.Bottom+DrawOfs.Y,workArea.Right+DrawOfs.X,workArea.Bottom+DrawOfs.Y);
+      if scaledArea.Left > lastWorkArea.Left then
+        FillRect(lastWorkArea.Left, scaledArea.Top, scaledArea.Left, scaledArea.Bottom);
+      if scaledArea.Top > lastWorkArea.Top then
+        FillRect(lastWorkArea.Left, lastWorkArea.Top, lastWorkArea.Right, scaledArea.Top);
+      if scaledArea.Right < lastWorkArea.Right then
+        FillRect(scaledArea.Right, scaledArea.Top, lastWorkArea.Right, scaledArea.Bottom);
+      if scaledArea.Bottom < lastWorkArea.Bottom then
+        FillRect(lastWorkArea.Left, scaledArea.Bottom, lastWorkArea.Right, lastWorkArea.Bottom);
     end;
   end else
-    PaintBlueAreaOnly(ACanvasOfs, AWorkArea);
+    PaintBlueAreaOnly(AWorkArea);
 end;
 
-procedure TImageView.PaintBlueAreaOnly(ACanvasOfs: TPoint; AWorkArea: TRect);
-var
-  DrawOfs: TPoint;
+procedure TImageView.PaintBlueAreaOnly(AWorkArea: TRect);
 begin
   if (AWorkArea.Right <= AWorkArea.Left) or (AWorkArea.Bottom <= AWorkArea.Top) then exit;
   with PictureCanvas do
   begin
     Brush.Color := WorkspaceColor;
-    DrawOfs := ACanvasOfs;
-    FillRect(AWorkArea.Left+DrawOfs.X,AWorkArea.Top+DrawOfs.Y,AWorkArea.Right+DrawOfs.X,AWorkArea.Bottom+DrawOfs.Y);
+    FillRect(AWorkArea);
   end;
   FLastPictureParameters.defined := false;
 end;
 
 constructor TImageView.Create(AInstance: TLazPaintCustomInstance; AZoom: TZoom;
-  ACanvas: TCanvas);
+  APaintBox: TGraphicControl);
 begin
   FInstance := AInstance;
   FZoom := AZoom;
-  FPictureCanvas := ACanvas;
 
-  FVirtualScreen := nil;
+  FPaintBox := APaintBox;
+  (FPaintBox as TPaintBox).OnMouseEnter:=@PaintBoxMouseEnter;
+  (FPaintBox as TPaintBox).OnMouseDown:= @PaintBoxMouseDown;
+  (FPaintBox as TPaintBox).OnMouseMove:= @PaintBoxMouseMove;
+  (FPaintBox as TPaintBox).OnMouseUp:=   @PaintBoxMouseUp;
+  (FPaintBox as TPaintBox).OnMouseWheel:=@PaintBoxMouseWheel;
+  (FPaintBox as TPaintBox).OnPaint:=@PaintBoxPaint;
+  //recursive calls
+  InFormMouseMove:= false;
+  InFormPaint := false;
   FLastPictureParameters.defined:= false;
   FSelectionHighlight := TSelectionHighlight.Create(Image);
   FShowSelection:= true;
   Image.OnSelectionChanged := @PictureSelectionChanged;
-  LazPaintInstance.ToolManager.OnToolRenderChanged:=@ToolManagerRenderChanged;
-  LazPaintInstance.ToolManager.BitmapToVirtualScreen := @BitmapToVirtualScreen;
+  Image.OnImageChanged.AddObserver(@ImageChanged);
+  if Assigned(LazPaintInstance.ToolManager) then
+  begin
+    LazPaintInstance.ToolManager.OnToolRenderChanged:=@ToolManagerRenderChanged;
+    LazPaintInstance.ToolManager.BitmapToVirtualScreen := @BitmapToVirtualScreen;
+  end;
+
+  //mouse status
+  btnLeftDown := false;
+  btnRightDown := false;
+  btnMiddleDown:= false;
+  FImagePos := EmptyPointF;
+  try
+    FTablet := TLazTablet.Create(nil);
+  except
+    on ex: exception do
+      FTablet := nil;
+  end;
 end;
 
 destructor TImageView.Destroy;
 begin
+  FreeAndNil(FTablet);
   if Assigned(LazPaintInstance.ToolManager) then
+  begin
+    LazPaintInstance.ToolManager.OnToolRenderChanged := nil;
     LazPaintInstance.ToolManager.BitmapToVirtualScreen := nil;
+  end;
+  Image.OnImageChanged.RemoveObserver(@ImageChanged);
   Image.OnSelectionChanged := nil;
   FreeAndNil(FSelectionHighlight);
   FreeAndNil(FVirtualScreen);
   inherited Destroy;
 end;
 
-procedure TImageView.DoPaint(ACanvasOfs: TPoint; AWorkArea: TRect; AShowNoPicture: boolean);
+procedure TImageView.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
+var picBoundsChanged: boolean;
+begin
+  picBoundsChanged := (FPaintBox.Left <> ALeft) or (FPaintBox.Top <> ATop) or
+      (FPaintBox.Width <> AWidth) or (FPaintBox.Height <> AHeight);
+  FPaintBox.SetBounds(ALeft, ATop, AWidth, AHeight);
+  if picBoundsChanged then
+    InvalidatePicture(True);
+end;
+
+function TImageView.CatchToolKeyDown(var AKey: Word): boolean;
 begin
-   if AShowNoPicture then
-   begin
-     PaintBlueAreaOnly(ACanvasOfs, AWorkArea);
-     exit;
-   end;
-   if FQueryPaintVirtualScreen and
-      (FLastPictureParameters.defined and
-       IsRectEmpty(GetRenderUpdateRectVS(False))) then
-     PaintVirtualScreenImplementation(ACanvasOfs, AWorkArea, rect(0,0,maxLongint,maxLongint))
-   else
-     PaintPictureImplementation(ACanvasOfs, AWorkArea, rect(0,0,maxLongint,maxLongint));
-   PaintBlueAreaImplementation(ACanvasOfs, AWorkArea);
+  FCatchPaintPicture:= true;
+  FPaintPictureCatched := false;
+  try
+    result := LazPaintInstance.ToolManager.ToolKeyDown(AKey) or FPaintPictureCatched;
+  finally
+    FCatchPaintPicture:= false;
+  end;
+end;
+
+function TImageView.CatchToolKeyUp(var AKey: Word): boolean;
+begin
+  FCatchPaintPicture:= true;
+  FPaintPictureCatched := false;
+  try
+     result := LazPaintInstance.ToolManager.ToolKeyUp(AKey) or FPaintPictureCatched;
+  finally
+    FCatchPaintPicture:= false;
+  end;
+end;
+
+function TImageView.CatchToolKeyPress(var AKey: TUTF8Char): boolean;
+begin
+  FCatchPaintPicture:= true;
+  FPaintPictureCatched := false;
+  try
+    result := LazPaintInstance.ToolManager.ToolKeyPress(AKey) or FPaintPictureCatched;
+  finally
+    FCatchPaintPicture:= false;
+  end;
+end;
+
+procedure TImageView.UpdatePicture;
+begin
+  DoUpdatePicture(WorkArea);
+  if not Image.OnImageChanged.DelayedStackUpdate then LazPaintInstance.InvalidateLayerStack;
+end;
+
+procedure TImageView.ShowNoPicture;
+begin
+  InShowNoPicture := true;
+  try
+    DoUpdatePicture(WorkArea);
+  finally
+    InShowNoPicture := false;
+  end;
+end;
+
+procedure TImageView.DoPaint(AWorkArea: TRect; AShowNoPicture: boolean);
+begin
+  if AShowNoPicture then
+    PaintBlueAreaOnly(AWorkArea)
+  else
+  begin
+    if FQueryPaintVirtualScreen and
+       (FLastPictureParameters.defined and
+        IsRectEmpty(GetRenderUpdateRectVS(False))) then
+       PaintVirtualScreenImplementation(AWorkArea, rect(0,0,maxLongint,maxLongint))
+    else
+      PaintPictureImplementation(AWorkArea, rect(0,0,maxLongint,maxLongint));
+    PaintBlueAreaImplementation(AWorkArea);
+  end;
+  if Assigned(FOnPaint) then FOnPaint(self);
+end;
+
+procedure TImageView.InvalidatePicture(AInvalidateAll: boolean);
+begin
+  DoInvalidatePicture(AInvalidateAll, WorkArea);
 end;
 
 procedure TImageView.ComputePictureParams(AWorkArea: TRect);
@@ -386,13 +706,13 @@ begin
   FLastPictureParameters.defined := true;
 end;
 
-procedure TImageView.OnZoomChanged(sender: TZoom; ANewZoom: single; AWorkArea: TRect);
+procedure TImageView.OnZoomChanged(sender: TZoom; ANewZoom: single);
 Var
   NewBitmapPos: TPointF;
 begin
   if sender.PositionDefined then
   begin
-    ComputePictureParams(AWorkArea);
+    ComputePictureParams(WorkArea);
     NewBitmapPos := FormToBitmap(sender.MousePosition.X,sender.MousePosition.Y);
     image.ImageOffset:= point(image.ImageOffset.X + round(NewBitmapPos.X-sender.BitmapPosition.X),
                               image.ImageOffset.Y + round(NewBitmapPos.Y-sender.BitmapPosition.Y));
@@ -486,14 +806,13 @@ begin
     result.bounds := EmptyRect;
 end;
 
-procedure TImageView.InvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect; AControlOfs: TPoint; AWinControl: TWinControl);
+procedure TImageView.DoInvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect);
 var
   area: TRect;
 begin
   area := GetRectToInvalidate(AInvalidateAll, AWorkArea);
   IntersectRect(area, area, AWorkArea);
-  OffsetRect(area, AControlOfs.X,AControlOfs.Y);
-  InvalidateRect(AWinControl.Handle,@area,False);
+  InvalidateControlRect(FPaintBox, area);
 end;
 
 procedure TImageView.PictureSelectionChanged(sender: TLazPaintImage; const ARect: TRect);
@@ -501,8 +820,7 @@ begin
   if Assigned(FSelectionHighlight) then FSelectionHighlight.NotifyChange(ARect);
 end;
 
-procedure TImageView.PaintVirtualScreenCursor(ACanvasOfs: TPoint; AWorkArea: TRect;
-                                              AWinControlOfs: TPoint; AWinControl: TWinControl);
+procedure TImageView.PaintVirtualScreenCursor(AWorkArea: TRect);
 var area: TRect;
 begin
   area := FPenCursorPos.bounds;
@@ -512,12 +830,11 @@ begin
                    FLastPictureParameters.virtualScreenArea.Top);
   {$IFDEF IMAGEVIEW_DIRECTUPDATE}
   OffsetRect(area, -FLastPictureParameters.virtualScreenArea.Left, -FLastPictureParameters.virtualScreenArea.Top);
-  PaintVirtualScreenImplementation(ACanvasOfs, AWorkArea, area);
+  PaintVirtualScreenImplementation(AWorkArea, area);
   {$ELSE}
   FQueryPaintVirtualScreen := True;
-  OffsetRect(area, AWinControlOfs.X,AWinControlOfs.Y);
-  InvalidateRect(AWinControl.Handle,@area,False);
-  AWinControl.Update;
+  InvalidateControlRect(FPaintBox, area);
+  FPaintBox.Update;
   FQueryPaintVirtualScreen := False;
   {$ENDIF}
 end;
@@ -544,8 +861,7 @@ begin
   end;
 end;
 
-procedure TImageView.UpdateCursor(X,Y: integer; ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl;
-                                 AWinControlOfs: TPoint; AWinControl: TWinControl);
+procedure TImageView.UpdateCursor(X,Y: integer);
 var virtualScreenPenCursorBefore: boolean;
     wantedCursor: TCursor;
 
@@ -567,13 +883,13 @@ begin
   FPenCursorVisible := false;
   wantedCursor := LazPaintInstance.ToolManager.Cursor;
   if LazPaintInstance.ToolManager.GetCurrentToolType in[ptPen,ptEraser,ptBrush,ptClone] then UseVSPenCursor;
-  if not PtInRect(AWorkArea, Point(X,Y)) then wantedCursor:= crDefault;
-  if AControl.Cursor <> wantedCursor then AControl.Cursor := wantedCursor;
+  if not PtInRect(WorkArea, Point(X,Y)) then wantedCursor:= crDefault;
+  if FPaintBox.Cursor <> wantedCursor then FPaintBox.Cursor := wantedCursor;
   if virtualScreenPenCursorBefore or FPenCursorVisible then
-    PaintVirtualScreenCursor(ACanvasOfs, AWorkArea, AWinControlOfs, AWinControl);
+    PaintVirtualScreenCursor(WorkArea);
 end;
 
-procedure TImageView.UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; AWinControl: TWinControl);
+procedure TImageView.DoUpdatePicture(AWorkArea: TRect);
 var
   updateArea: TRect;
   {$IFDEF IMAGEVIEW_DIRECTUPDATE}prevVSArea: TRect;{$ENDIF}
@@ -588,17 +904,49 @@ begin
   {$IFDEF IMAGEVIEW_DIRECTUPDATE}
   if FLastPictureParameters.defined then
     OffsetRect(updateArea, -FLastPictureParameters.virtualScreenArea.Left,-FLastPictureParameters.virtualScreenArea.Top);
-  PaintPictureImplementation(ACanvasOfs, AWorkArea, updateArea);
+  PaintPictureImplementation(AWorkArea, updateArea);
   if prevVSArea <> FLastPictureParameters.virtualScreenArea then
-    PaintBlueAreaImplementation(ACanvasOfs, AWorkArea);
+    PaintBlueAreaImplementation(AWorkArea);
   {$ELSE}
   if IntersectRect(updateArea, updateArea, AWorkArea) then
   begin
-    InvalidateRect(AWinControl.Handle, @updateArea, false);
-    AWinControl.Update;
+    InvalidateControlRect(FPaintBox, updateArea);
+    FPaintBox.Update;
   end;
   {$ENDIF}
 end;
 
+procedure TImageView.ReleaseMouseButtons(Shift: TShiftState);
+begin
+  if not (ssLeft in Shift) and btnLeftDown then
+  begin
+    btnLeftDown := false;
+    if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
+  end;
+  if not (ssRight in Shift) and btnRightDown then
+  begin
+    btnRightDown := false;
+    if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
+  end;
+  if not (ssMiddle in Shift) and btnMiddleDown then
+  begin
+    btnMiddleDown := false;
+    if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
+  end;
+  if not btnLeftDown and not btnRightDown then
+  begin
+    FCanCompressOrUpdateStack := true;
+    Image.OnImageChanged.DelayedStackUpdate := False;
+  end;
+end;
+
+function TImageView.GetCurrentPressure: single;
+begin
+  if Assigned(FTablet) and FTablet.Present and FTablet.Entering and (FTablet.Max > 0) then
+    result := FTablet.Pressure/FTablet.Max
+  else
+    result := 1;
+end;
+
 end.
 

+ 194 - 31
lazpaint/umainformlayout.pas

@@ -6,8 +6,10 @@ unit UMainFormLayout;
 interface
 
 uses
-  Classes, SysUtils, UMenu, Forms, LazPaintType, UZoom, ExtCtrls, ComCtrls,
-  Menus, UPaletteToolbar, BGRABitmapTypes, Controls;
+  Classes, LCLType, SysUtils, UMenu, Forms, LazPaintType, UZoom, ExtCtrls, ComCtrls,
+  Menus, UPaletteToolbar, BGRABitmapTypes, Controls, UImageView;
+
+const SelectionPaintDelay = 100/(1000*60*60*24);
 
 type
   TOnPictureAreaChange = procedure(ASender: TObject; ANewArea: TRect) of object;
@@ -18,12 +20,15 @@ type
   { TMainFormLayout }
 
   TMainFormLayout = class(TCustomMainFormLayout)
-    procedure PaletteVisibilityChangedByUser(Sender: TObject);
   private
     FForm: TForm;
+    FOnPaintPicture: TNotifyEvent;
+    FOnPictureMouseBefore: TPictureMouseBeforeEvent;
+    FOnPictureMouseMove: TPictureMouseMoveEvent;
+    FOnToolbarUpdate: TNotifyEvent;
+    FZoom: TZoom;
     FMenu: TMainFormMenu;
     FLazPaintInstance: TLazPaintCustomInstance;
-    FOnPictureAreaChange: TOnPictureAreaChange;
     FToolBoxDocking: TToolWindowDocking;
     FInSetToolBoxDocking: boolean;
     FPanelToolBox: TPanel;
@@ -36,11 +41,23 @@ type
     FDarkTheme: boolean;
     FDockedControlsPanel: TPanel;
     FDockedChooseColorSplitter: TSplitter;
+    FPaintBox: TPaintBox;
+    FImageView: TImageView;
+    function GetFillSelectionHighlight: boolean;
+    function GetMouseButtonState: TShiftState;
     function GetPaletteVisible: boolean;
     function GetPopupToolbox: TPopupMenu;
     function GetStatusBarVisible: boolean;
     function GetStatusText: string;
     function GetToolBoxVisible: boolean;
+    function GetUpdatingPopup: boolean;
+    procedure ImageToolbarUpdate(Sender: TObject);
+    procedure ImageViewMouseBefore(ASender: TObject; AShift: TShiftState);
+    procedure ImageViewMouseMove(Sender: TObject; APosition: TPointF);
+    procedure ImageViewOnPaint(Sender: TObject);
+    procedure SetFillSelectionHighlight(AValue: boolean);
+    procedure SetOnPaintPicture(AValue: TNotifyEvent);
+    procedure SetUpdatingPopup(AValue: boolean);
     procedure StatusBar_Paint(Sender: TObject);
     procedure ToolboxGroupMainButton_MouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X,
       {%H-}Y: Integer);
@@ -58,8 +75,9 @@ type
     procedure ToolboxGroupToolbarButton_MouseEnter(Sender: TObject);
     procedure ToolboxGroupToolbarButton_MouseLeave(Sender: TObject);
     procedure ToolboxSimpleButton_Click(Sender: TObject);
+    procedure PaletteVisibilityChangedByUser(Sender: TObject);
+    procedure ZoomChanged(sender: TZoom; ANewZoom: single);
   protected
-    FLastWorkArea: TRect;
     FDockedToolboxGroup: array of record
         Button: TToolButton;
         Panel: TPanel;
@@ -68,7 +86,6 @@ type
       end;
     function GetWorkArea: TRect; override;
     function GetWorkAreaAt(AStage: TLayoutStage): TRect;
-    procedure RaisePictureAreaChange;
     procedure DoArrange;
     procedure ApplyTheme;
     function DockedControlsPanelWidth: integer;
@@ -77,9 +94,14 @@ type
     procedure HideToolboxGroup(AGroupIndex: integer);
     procedure HideToolboxGroups;
   public
-    constructor Create(AForm: TForm);
+    DelayedPaintPicture: boolean;
+    constructor Create(AForm: TForm; AZoom: TZoom);
     destructor Destroy; override;
+    function CatchToolKeyDown(var AKey: Word): boolean;
+    function CatchToolKeyPress(var AKey: TUTF8Char): boolean;
+    function CatchToolKeyUp(var AKey: Word): boolean;
     procedure Arrange;
+    procedure CheckDelayedUpdate;
     procedure DockedToolBoxAddButton(AAction: TBasicAction);
     procedure DockedToolBoxAddGroup(AActions: array of TBasicAction);
     procedure DockedToolBoxSetImages(AImages: TImageList);
@@ -87,10 +109,12 @@ type
     procedure RemoveColorFromPalette(AColor : TBGRAPixel);
     procedure AddDockedControl(AControl: TControl);
     procedure RemoveDockedControl(AControl: TControl);
+    procedure PaintPictureNow;
+    procedure InvalidatePicture(AInvalidateAll: boolean);
+    procedure ShowNoPicture;
     property Menu: TMainFormMenu read FMenu write FMenu;
     property ToolBoxDocking: TToolWindowDocking read FToolBoxDocking write SetToolBoxDocking;
     property ToolBoxVisible: boolean read GetToolBoxVisible write SetToolBoxVisible;
-    property OnPictureAreaChange: TOnPictureAreaChange read FOnPictureAreaChange write FOnPictureAreaChange;
     property LazPaintInstance: TLazPaintCustomInstance read FLazPaintInstance write SetLazPaintInstance;
     property ToolboxPopup: TPopupMenu read GetPopupToolbox write SetPopupToolbox;
     property PaletteVisible: boolean read GetPaletteVisible write SetPaletteVisible;
@@ -98,6 +122,13 @@ type
     property StatusText: string read GetStatusText write SetStatusText;
     property DefaultToolboxDocking: TToolWindowDocking read GetDefaultToolboxDocking;
     property DarkTheme: boolean read FDarkTheme write SetDarkTheme;
+    property OnPaintPicture: TNotifyEvent read FOnPaintPicture write SetOnPaintPicture;
+    property OnToolbarUpdate: TNotifyEvent read FOnToolbarUpdate write FOnToolbarUpdate;
+    property OnPictureMouseMove: TPictureMouseMoveEvent read FOnPictureMouseMove write FOnPictureMouseMove;
+    property OnPictureMouseBefore: TPictureMouseBeforeEvent read FOnPictureMouseBefore write FOnPictureMouseBefore;
+    property FillSelectionHighlight: boolean read GetFillSelectionHighlight write SetFillSelectionHighlight;
+    property UpdatingPopup: boolean read GetUpdatingPopup write SetUpdatingPopup;
+    property MouseButtonState: TShiftState read GetMouseButtonState;
   end;
 
 function ToolWindowDockingToStr(AValue: TToolWindowDocking): string;
@@ -141,9 +172,11 @@ end;
 
 { TMainFormLayout }
 
-constructor TMainFormLayout.Create(AForm: TForm);
+constructor TMainFormLayout.Create(AForm: TForm; AZoom: TZoom);
 begin
   FForm := AForm;
+  FZoom := AZoom;
+  FZoom.OnZoomChanged:=@ZoomChanged;
   FPanelToolBox := TPanel.Create(nil);
   FPanelToolBox.BevelInner := bvNone;
   FPanelToolBox.BevelOuter := bvNone;
@@ -183,11 +216,17 @@ begin
   FStatusTextSplit := TStringList.Create;
   FForm.InsertControl(FStatusBar);
 
+  FPaintBox := TPaintBox.Create(nil);
+  FForm.InsertControl(FPaintBox);
+  FImageView := nil;
+
   ApplyTheme;
 end;
 
 destructor TMainFormLayout.Destroy;
 begin
+  FreeAndNil(FImageView);
+  FreeAndNil(FPaintBox);
   FreeAndNil(FDockedControlsPanel);
   FreeAndNil(FStatusBar);
   FreeAndNil(FStatusTextSplit);
@@ -197,6 +236,27 @@ begin
   inherited Destroy;
 end;
 
+function TMainFormLayout.CatchToolKeyDown(var AKey: Word): boolean;
+begin
+  if Assigned(FImageView) then
+    result := FImageView.CatchToolKeyDown(AKey)
+    else result := false;
+end;
+
+function TMainFormLayout.CatchToolKeyUp(var AKey: Word): boolean;
+begin
+  if Assigned(FImageView) then
+    result := FImageView.CatchToolKeyUp(AKey)
+    else result := false;
+end;
+
+function TMainFormLayout.CatchToolKeyPress(var AKey: TUTF8Char): boolean;
+begin
+  if Assigned(FImageView) then
+    result := FImageView.CatchToolKeyPress(AKey)
+    else result := false;
+end;
+
 procedure TMainFormLayout.SetToolBoxDocking(AValue: TToolWindowDocking);
 begin
   if FInSetToolBoxDocking or (FToolBoxDocking=AValue) then Exit;
@@ -209,7 +269,6 @@ begin
       FLazPaintInstance.Config.SetDefaultToolboxDocking(ToolWindowDockingToStr(AValue));
   end;
   DoArrange;
-  RaisePictureAreaChange;
   FInSetToolBoxDocking := false;
 end;
 
@@ -218,6 +277,56 @@ begin
   result := LazPaintInstance.ToolboxVisible;
 end;
 
+function TMainFormLayout.GetUpdatingPopup: boolean;
+begin
+  if Assigned(FImageView) then
+    result := FImageView.UpdatingPopup
+    else result := False;
+end;
+
+procedure TMainFormLayout.ImageToolbarUpdate(Sender: TObject);
+begin
+  if Assigned(FOnToolbarUpdate) then
+    FOnToolbarUpdate(self);
+end;
+
+procedure TMainFormLayout.ImageViewMouseBefore(ASender: TObject;
+  AShift: TShiftState);
+begin
+  if Assigned(FOnPictureMouseBefore) then
+    FOnPictureMouseBefore(self, AShift);
+end;
+
+procedure TMainFormLayout.ImageViewMouseMove(Sender: TObject; APosition: TPointF);
+begin
+  if Assigned(FOnPictureMouseMove) then
+    FOnPictureMouseMove(self, APosition);
+end;
+
+procedure TMainFormLayout.ImageViewOnPaint(Sender: TObject);
+begin
+  if Assigned(FOnPaintPicture) then
+    FOnPaintPicture(self);
+end;
+
+procedure TMainFormLayout.SetFillSelectionHighlight(AValue: boolean);
+begin
+  if Assigned(FImageView) then
+    FImageView.FillSelectionHighlight := AValue;
+end;
+
+procedure TMainFormLayout.SetOnPaintPicture(AValue: TNotifyEvent);
+begin
+  if FOnPaintPicture=AValue then Exit;
+  FOnPaintPicture:=AValue;
+end;
+
+procedure TMainFormLayout.SetUpdatingPopup(AValue: boolean);
+begin
+  if Assigned(FImageView) then
+    FImageView.UpdatingPopup := AValue;
+end;
+
 procedure TMainFormLayout.StatusBar_Paint(Sender: TObject);
 var
   colWidth, spacing, i, x: Integer;
@@ -287,6 +396,12 @@ begin
   FLazPaintInstance:=AValue;
   FPaletteToolbar.LazPaintInstance:= AValue;
   FStatusBarVisible := LazPaintInstance.Config.GetStatusBarVisible;
+  if Assigned(FImageView) then FreeAndNil(FImageView);
+  FImageView := TImageView.Create(FLazPaintInstance, FZoom, FPaintBox);
+  FImageView.OnPaint:=@ImageViewOnPaint;
+  FImageView.OnToolbarUpdate:=@ImageToolbarUpdate;
+  FImageView.OnMouseMove:=@ImageViewMouseMove;
+  FImageView.OnMouseBefore:=@ImageViewMouseBefore;
 end;
 
 procedure TMainFormLayout.SetPaletteVisible(AValue: boolean);
@@ -316,11 +431,36 @@ begin
   Arrange;
 end;
 
+procedure TMainFormLayout.ZoomChanged(sender: TZoom; ANewZoom: single);
+begin
+  if Assigned(FImageView) then
+  begin
+    if not LazPaintInstance.Image.SelectionMaskEmpty then
+      FImageView.ShowSelection := false;
+    FImageView.OnZoomChanged(sender, ANewZoom);
+  end;
+  PaintPictureNow;
+end;
+
 function TMainFormLayout.GetPaletteVisible: boolean;
 begin
   result := FPaletteToolbar.Visible;
 end;
 
+function TMainFormLayout.GetFillSelectionHighlight: boolean;
+begin
+  if Assigned(FImageView) then
+    result := FillSelectionHighlight
+    else result := false;
+end;
+
+function TMainFormLayout.GetMouseButtonState: TShiftState;
+begin
+  if Assigned(FImageView) then
+    result := FImageView.MouseButtonState
+    else result := [];
+end;
+
 procedure TMainFormLayout.SetPopupToolbox(AValue: TPopupMenu);
 begin
   FPanelToolBox.PopupMenu := AValue;
@@ -440,12 +580,6 @@ begin
   if AStage = lsAfterStatusBar then exit;
 end;
 
-procedure TMainFormLayout.RaisePictureAreaChange;
-begin
-  if Assigned(FOnPictureAreaChange) then
-    FOnPictureAreaChange(self, WorkArea);
-end;
-
 procedure TMainFormLayout.DoArrange;
 var nbY,nbX,w,i: integer;
   updateChooseColorHeight: Boolean;
@@ -522,6 +656,9 @@ begin
     end;
   end
   else FStatusBar.Visible := false;
+  if Assigned(FImageView) then
+    with WorkArea do
+    FImageView.SetBounds(Left, Top, Width, Height);
 end;
 
 procedure TMainFormLayout.ApplyTheme;
@@ -622,28 +759,36 @@ begin
 end;
 
 procedure TMainFormLayout.Arrange;
-var picAreaBeforeArrange,newPicArea: TRect;
 begin
-  picAreaBeforeArrange := WorkArea;
   DoArrange;
   HideToolboxGroups;
-  newPicArea := WorkArea;
-  if (newPicArea.Left <> picAreaBeforeArrange.Left) or
-     (newPicArea.Top <> picAreaBeforeArrange.Top) or
-     (newPicArea.Right <> picAreaBeforeArrange.Right) or
-     (newPicArea.Bottom <> picAreaBeforeArrange.Bottom) or
-     (newPicArea.Left <> FLastWorkArea.Left) or
-     (newPicArea.Top <> FLastWorkArea.Top) or
-     (newPicArea.Right <> FLastWorkArea.Right) or
-     (newPicArea.Bottom <> FLastWorkArea.Bottom) then
-  begin
-    RaisePictureAreaChange;
-    FLastWorkArea := newPicArea;
-  end;
   if Assigned(FMenu) then
     FMenu.RepaintToolbar;
 end;
 
+procedure TMainFormLayout.CheckDelayedUpdate;
+begin
+  if (LazPaintInstance = nil) or (FImageView = nil) then exit;
+
+  if FImageView.CanCompressOrUpdateStack and LazPaintInstance.UpdateStackOnTimer then
+  begin
+    LazPaintInstance.NotifyStackChange;
+    LazPaintInstance.UpdateStackOnTimer := false;
+  end else
+  begin
+    if FImageView.CanCompressOrUpdateStack then LazPaintInstance.Image.CompressUndo;
+  end;
+
+  if DelayedPaintPicture or LazPaintInstance.ToolManager.ToolUpdateNeeded or
+   (not FImageView.ShowSelection and
+    (Now > FImageView.LastPaintDate+SelectionPaintDelay) ) then
+  begin
+    if LazPaintInstance.ToolManager.ToolUpdateNeeded then LazPaintInstance.ToolManager.ToolUpdate;
+    if Assigned(FImageView) then FImageView.ShowSelection := true;
+    PaintPictureNow;
+  end;
+end;
+
 procedure TMainFormLayout.DockedToolBoxAddButton(AAction: TBasicAction);
 var button: TToolButton;
 begin
@@ -775,5 +920,23 @@ begin
   end;
 end;
 
+procedure TMainFormLayout.PaintPictureNow;
+begin
+  if Assigned(FImageView) then
+    FImageView.UpdatePicture;
+end;
+
+procedure TMainFormLayout.InvalidatePicture(AInvalidateAll: boolean);
+begin
+  if Assigned(FImageView) then
+    FImageView.InvalidatePicture(AInvalidateAll);
+end;
+
+procedure TMainFormLayout.ShowNoPicture;
+begin
+  if Assigned(FImageView) then
+    FImageView.ShowNoPicture;
+end;
+
 end.