SourceEditorIntf: New methods to add custom panels to the SourceEditor-Window.

This commit is contained in:
Martin 2023-06-12 23:20:48 +02:00
parent 691e47cc00
commit 68910f5711
3 changed files with 419 additions and 12 deletions

View File

@ -16,7 +16,7 @@ interface
uses
Classes,
// LCL
LCLType, Forms, Controls, Graphics,
LCLType, Forms, Controls, Graphics, ComCtrls,
// LazUtils
Laz2_XMLCfg, LazStringUtils,
// BuildIntf
@ -224,6 +224,64 @@ type
{%H-}ItemSelected: boolean; {%H-}Index: integer): TPoint; virtual;
end;
TSourceEditorStatusPanelInterface = class;
TDrawSourceEditPanelEvent = procedure(APanel: TSourceEditorStatusPanelInterface;
ACanvas: TCanvas; const Rect: TRect) of object;
{ TSourceEditorStatusPanelInterface }
TSourceEditorStatusPanelInterface = class
private
FOnClick: TNotifyEvent;
FOnContextPopup: TContextPopupEvent;
FOnDoubleClick: TNotifyEvent;
FOnDrawPanel: TDrawSourceEditPanelEvent;
FOwner: TClass;
FTag: PtrUInt;
FOnDestroy: TNotifyEvent;
FOnResize: TNotifyEvent;
protected
function GetAlignment: TAlignment; virtual; abstract;
function GetBevel: TStatusPanelBevel; virtual; abstract;
function GetBidiMode: TBiDiMode; virtual; abstract;
function GetHeight: integer; virtual; abstract;
function GetScreenBounds: TRect;virtual; abstract;
function GetText: string; virtual; abstract;
function GetVisible: boolean; virtual; abstract;
function GetWidth: integer; virtual; abstract;
procedure SetAlignment(AValue: TAlignment); virtual; abstract;
procedure SetBevel(AValue: TStatusPanelBevel); virtual; abstract;
procedure SetBidiMode(AValue: TBiDiMode); virtual; abstract;
procedure SetText(AValue: string); virtual; abstract;
procedure SetOnDrawPanel(AValue: TDrawSourceEditPanelEvent); virtual;
procedure SetVisible(AValue: boolean); virtual; abstract;
public
constructor Create(AnOwner: TClass; ATag: PtrUInt);
property Owner: TClass read FOwner;
property Tag: PtrUInt read FTag;
property Alignment: TAlignment read GetAlignment write SetAlignment;
property Bevel: TStatusPanelBevel read GetBevel write SetBevel;
property BidiMode: TBiDiMode read GetBidiMode write SetBidiMode;
property Text: string read GetText write SetText;
property Visible: boolean read GetVisible write SetVisible;
function RequestWidth(AWidth: Integer): Integer; virtual; abstract;
property Height: integer read GetHeight;
property Width: integer read GetWidth;
property ScreenBounds: TRect read GetScreenBounds;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
property OnResize: TNotifyEvent read FOnResize write FOnResize;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
property OnDoubleClick: TNotifyEvent read FOnDoubleClick write FOnDoubleClick;
property OnContextPopup: TContextPopupEvent read FOnContextPopup write FOnContextPopup;
property OnDrawPanel: TDrawSourceEditPanelEvent read FOnDrawPanel write SetOnDrawPanel; // change draw mode
end;
{ TSourceEditorWindowInterface }
TSourceEditorWindowInterface = class(TForm)
@ -236,6 +294,9 @@ type
procedure SetActiveEditor(const AValue: TSourceEditorInterface); virtual; abstract;
procedure SetBaseCaption(AValue: String); virtual; abstract;
function GetWindowID: Integer; virtual; abstract;
function GetStatusPanel(AnOwner: TClass; AnIdx: integer): TSourceEditorStatusPanelInterface; virtual; abstract;
function GetStatusPanelTagged(AnOwner: TClass; ATag: PtrUInt; AnIdx: integer): TSourceEditorStatusPanelInterface; virtual; abstract;
public
procedure IncUpdateLock; virtual; abstract;
procedure DecUpdateLock; virtual; abstract;
@ -251,6 +312,12 @@ type
procedure AddUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent; const AsLast: Boolean = True); virtual; abstract;
procedure RemoveUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent); virtual; abstract;
property BaseCaption: String read GetBaseCaption write SetBaseCaption;
function AddStatusPanel(AnOwner: TClass; ATag: PtrUInt = 0): TSourceEditorStatusPanelInterface; virtual; abstract;
function StatusPanelCount(AnOwner: TClass): integer; virtual; abstract;
function StatusPanelTaggedCount(AnOwner: TClass; ATag: PtrUInt): integer; virtual; abstract;
property StatusPanel[AnOwner: TClass; AnIdx: integer]: TSourceEditorStatusPanelInterface read GetStatusPanel;
property StatusPanelTagged[AnOwner: TClass; ATag: PtrUInt; AnIdx: integer]: TSourceEditorStatusPanelInterface read GetStatusPanelTagged;
end;
TSemChangeReason = (
@ -894,6 +961,23 @@ begin
Result:=Point(0,0);
end;
{ TSourceEditorStatusPanelInterface }
procedure TSourceEditorStatusPanelInterface.SetOnDrawPanel(
AValue: TDrawSourceEditPanelEvent);
begin
if FOnDrawPanel = AValue then Exit;
FOnDrawPanel := AValue;
end;
constructor TSourceEditorStatusPanelInterface.Create(AnOwner: TClass;
ATag: PtrUInt);
begin
FOwner := AnOwner;
FTag := ATag;
inherited Create;
end;
{ TSourceMarkling }
constructor TSourceMarkling.Create(aProducer: TSourceMarklingProducer; TheID,

View File

@ -8,7 +8,8 @@ object SourceNotebook: TSourceNotebook
ClientHeight = 300
ClientWidth = 400
OnMouseUp = FormMouseUp
LCLVersion = '2.1.0.0'
OnResize = FormResize
LCLVersion = '2.3.0.0'
object StatusBar: TStatusBar
Left = 0
Height = 23
@ -40,8 +41,8 @@ object SourceNotebook: TSourceNotebook
OnDrawPanel = StatusBarDrawPanel
end
object StatusPopUpMenu: TPopupMenu
left = 48
top = 36
Left = 48
Top = 36
object GoToLineMenuItem: TMenuItem
Caption = 'Goto line ...'
OnClick = GoToLineMenuItemClick

View File

@ -44,7 +44,7 @@ uses
{$ENDIF}
SynEditMouseCmds,
// RTL + FCL
Classes, SysUtils, StrUtils, Types, Contnrs, Math, RegExpr, Laz_AVL_Tree,
Classes, SysUtils, StrUtils, Types, Contnrs, Math, fgl, RegExpr, Laz_AVL_Tree,
// LCL
Controls, Forms, ComCtrls, StdCtrls, Graphics, Dialogs, Extctrls, Menus,
LCLProc, LCLType, LCLIntf, ClipBrd, HelpIntfs, Messages, LMessages,
@ -620,6 +620,43 @@ type
TBrowseEditorTabHistoryDialog = class;
{ TSourceEditorStatusPanel }
TSourceEditorStatusPanel = class(TSourceEditorStatusPanelInterface)
private
FPanel: TStatusPanel;
FSrcNotebook: TSourceNotebook;
FVisible: Boolean;
FWidth: integer;
procedure SetRealWidth(AWidth: integer);
protected
function GetAlignment: TAlignment; override;
function GetBevel: TStatusPanelBevel; override;
function GetBidiMode: TBiDiMode; override;
function GetHeight: integer; override;
function GetScreenBounds: TRect;override;
function GetText: string; override;
function GetVisible: boolean; override;
function GetWidth: integer; override;
procedure SetAlignment(AValue: TAlignment); override;
procedure SetBevel(AValue: TStatusPanelBevel); override;
procedure SetBidiMode(AValue: TBiDiMode); override;
procedure SetText(AValue: string); override;
procedure SetOnDrawPanel(AValue: TDrawSourceEditPanelEvent); override;
procedure SetVisible(AValue: boolean); override;
public
destructor Destroy; override;
function RequestWidth(AWidth: Integer): Integer; override;
end;
{ TSourceEditorStatusPanelList }
TSourceEditorStatusPanelList = class(specialize TFPGObjectList<TSourceEditorStatusPanel>)
public
function GetItemForPanel(APanel: TStatusPanel): TSourceEditorStatusPanel;
end;
{ TSourceNotebook }
TSourceNotebook = class(TSourceEditorWindowInterface)
@ -635,6 +672,7 @@ type
procedure FindOverloadsMenuItemClick(Sender: TObject);
procedure FormMouseUp(Sender: TObject; {%H-}Button: TMouseButton;
{%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
procedure FormResize(Sender: TObject);
procedure GoToLineMenuItemClick(Sender: TObject);
procedure HighlighterClicked(Sender: TObject);
procedure InsertCharacter(const C: TUTF8Char);
@ -671,8 +709,10 @@ type
FIsClosing: Boolean;
FSrcEditsSortedForFilenames: TAvlTree; // TSourceEditorInterface sorted for Filename
TabPopUpMenu, SrcPopUpMenu, DbgPopUpMenu: TPopupMenu;
FStatusPanels: TSourceEditorStatusPanelList;
procedure ApplyPageIndex;
procedure ExecuteEditorItemClick(Sender: TObject);
procedure CalculateStatusBarSizes;
public
procedure DeleteBreakpointClicked(Sender: TObject);
procedure ToggleBreakpointClicked(Sender: TObject);
@ -752,6 +792,11 @@ type
procedure DoShow; override;
procedure DoHide; override;
function GetWindowID: Integer; override;
function GetStatusPanel(AnOwner: TClass; AnIdx: integer
): TSourceEditorStatusPanelInterface; override;
function GetStatusPanelTagged(AnOwner: TClass; ATag: PtrUInt; AnIdx: integer
): TSourceEditorStatusPanelInterface; override;
protected
function GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; override;
function GetBaseCaption: String; override;
@ -873,6 +918,9 @@ type
procedure MoveEditor(OldPageIndex, NewWindowIndex, NewPageIndex: integer);
procedure UpdateStatusBar;
function AddStatusPanel(AnOwner: TClass; ATag: PtrUInt = 0): TSourceEditorStatusPanelInterface; override;
function StatusPanelCount(AnOwner: TClass): integer; override;
function StatusPanelTaggedCount(AnOwner: TClass; ATag: PtrUInt): integer; override;
procedure ClearExecutionLines;
procedure ClearExecutionMarks;
@ -6538,6 +6586,144 @@ begin
EditorComponent.CommandProcessor(TSynEditorCommand(EditorCommand),' ',nil);
end;
{ TSourceEditorStatusPanel }
function TSourceEditorStatusPanel.GetAlignment: TAlignment;
begin
Result := FPanel.Alignment;
end;
function TSourceEditorStatusPanel.GetBevel: TStatusPanelBevel;
begin
Result := FPanel.Bevel;
end;
function TSourceEditorStatusPanel.GetBidiMode: TBiDiMode;
begin
Result := FPanel.BidiMode;
end;
function TSourceEditorStatusPanel.GetHeight: integer;
begin
Result := FPanel.StatusBar.ClientHeight;
end;
function TSourceEditorStatusPanel.GetScreenBounds: TRect;
var
b: TRect;
i: Integer;
begin
b.Top := FPanel.StatusBar.ClientRect.Top;
b.Bottom := FPanel.StatusBar.ClientRect.Bottom;
b.Left := 0;
for i := 0 to FPanel.Index - 1 do
b.Left := b.Left + FPanel.StatusBar.Panels[i].Width;
b.Right := b.Left + FPanel.Width;
Result := FSrcNotebook.StatusBar.ClientToScreen(b);
end;
function TSourceEditorStatusPanel.GetText: string;
begin
Result := FPanel.Text;
end;
function TSourceEditorStatusPanel.GetVisible: boolean;
begin
Result := FVisible;
end;
function TSourceEditorStatusPanel.GetWidth: integer;
begin
Result := FPanel.Width;
end;
procedure TSourceEditorStatusPanel.SetAlignment(AValue: TAlignment);
begin
FPanel.Alignment := AValue;
end;
procedure TSourceEditorStatusPanel.SetBevel(AValue: TStatusPanelBevel);
begin
FPanel.Bevel := AValue;
end;
procedure TSourceEditorStatusPanel.SetBidiMode(AValue: TBiDiMode);
begin
FPanel.BidiMode := AValue;
end;
procedure TSourceEditorStatusPanel.SetText(AValue: string);
begin
FPanel.Text := AValue;
end;
procedure TSourceEditorStatusPanel.SetOnDrawPanel(
AValue: TDrawSourceEditPanelEvent);
begin
inherited SetOnDrawPanel(AValue);
if AValue = nil then
FPanel.Style := psText
else
FPanel.Style := psOwnerDraw;
end;
procedure TSourceEditorStatusPanel.SetVisible(AValue: boolean);
begin
if FVisible = AValue then
exit;
FVisible := AValue;
FSrcNotebook.CalculateStatusBarSizes;
end;
destructor TSourceEditorStatusPanel.Destroy;
begin
if (FSrcNotebook <> nil) and (FSrcNotebook.FStatusPanels <> nil) then
FSrcNotebook.FStatusPanels.Remove(Self);
if OnDestroy <> nil then
OnDestroy(Self);
inherited Destroy;
end;
function TSourceEditorStatusPanel.RequestWidth(AWidth: Integer): Integer;
begin
if (not FVisible) or (AWidth = FPanel.Width) then begin
FWidth := AWidth;
exit;
end;
FWidth := AWidth;
FSrcNotebook.CalculateStatusBarSizes;
Result := FPanel.Width;
end;
procedure TSourceEditorStatusPanel.SetRealWidth(AWidth: integer);
begin
assert(FVisible or (AWidth = 0), 'TSourceEditorStatusPanel.SetRealWidth: FVisible or (AWidth = 0)');
if (FPanel.Width = AWidth) then
exit;
FPanel.Width := AWidth;
if OnResize <> nil then
OnResize(Self);
end;
{ TSourceEditorStatusPanelList }
function TSourceEditorStatusPanelList.GetItemForPanel(APanel: TStatusPanel
): TSourceEditorStatusPanel;
var
i: Integer;
begin
i := Count - 1;
while (i >= 0) do begin
Result := Items[i];
if Result.FPanel = APanel then
exit;
end;
Result := nil;
end;
{------------------------------------------------------------------------}
{ TSourceNotebook }
@ -6595,6 +6781,8 @@ begin
FUpdateTabAndPageTimer.Interval := 500;
FUpdateTabAndPageTimer.OnTimer := @UpdateTabsAndPageTimeReached;
FStatusPanels := TSourceEditorStatusPanelList.Create(True);
CreateNotebook;
Application.AddOnDeactivateHandler(@OnApplicationDeactivate);
@ -6630,6 +6818,7 @@ begin
Application.RemoveOnDeactivateHandler(@OnApplicationDeactivate);
Application.RemoveOnMinimizeHandler(@OnApplicationDeactivate);
FreeAndNil(FNotebook);
FreeAndNil(FStatusPanels); // first NIL, so items don't remove themself
inherited Destroy;
DebugLnExit(SRCED_CLOSE, ['TSourceNotebook.Destroy ']);
@ -7214,6 +7403,37 @@ begin
Result := FWindowID;
end;
function TSourceNotebook.GetStatusPanel(AnOwner: TClass; AnIdx: integer
): TSourceEditorStatusPanelInterface;
var
i: Integer;
begin
Result := nil;
for i := 0 to FStatusPanels.Count - 1 do
if (FStatusPanels[i].Owner = AnOwner)
then begin
if AnIdx = 0 then
exit(FStatusPanels[i]);
dec(AnIdx);
end;
end;
function TSourceNotebook.GetStatusPanelTagged(AnOwner: TClass; ATag: PtrUInt;
AnIdx: integer): TSourceEditorStatusPanelInterface;
var
i: Integer;
begin
Result := nil;
for i := 0 to FStatusPanels.Count - 1 do
if (FStatusPanels[i].Owner = AnOwner) and
(FStatusPanels[i].Tag = ATag)
then begin
if AnIdx = 0 then
exit(FStatusPanels[i]);
dec(AnIdx);
end;
end;
procedure TSourceNotebook.SetPageIndex(AValue: Integer);
begin
if (fPageIndex = AValue) and (FNotebook.PageIndex = AValue) then begin
@ -8218,41 +8438,74 @@ end;
procedure TSourceNotebook.StatusBarClick(Sender: TObject);
var
P: TPoint;
i: Integer;
pnl: TSourceEditorStatusPanel;
begin
P := StatusBar.ScreenToClient(Mouse.CursorPos);
if StatusBar.GetPanelIndexAt(P.X, P.Y) = 1 then
i := StatusBar.GetPanelIndexAt(P.X, P.Y);
if i = 1 then
EditorMacroForRecording.Stop;
if i >= 0 then begin
pnl := FStatusPanels.GetItemForPanel(StatusBar.Panels[i]);
if (pnl <> nil) and (pnl.OnClick <> nil) then
pnl.OnClick(Self);
end;
end;
procedure TSourceNotebook.StatusBarDblClick(Sender: TObject);
var
P: TPoint;
i: Integer;
pnl: TSourceEditorStatusPanel;
begin
P := StatusBar.ScreenToClient(Mouse.CursorPos);
i := StatusBar.GetPanelIndexAt(P.X, P.Y);
case StatusBar.GetPanelIndexAt(P.X, P.Y) of // Based on panel:
0: GoToLineMenuItemClick(Nil); // Show "Goto Line" dialog.
4: OpenFolderMenuItemClick(Nil); // Show system file manager on file's folder.
else
if i >= 0 then begin
pnl := FStatusPanels.GetItemForPanel(StatusBar.Panels[i]);
if (pnl <> nil) and (pnl.OnDoubleClick <> nil) then
pnl.OnDoubleClick(Self);
end;
end;
end;
procedure TSourceNotebook.StatusBarContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
var
Pnl: Integer;
i: Integer;
pnl: TSourceEditorStatusPanel;
begin
Pnl := StatusBar.GetPanelIndexAt(MousePos.X, MousePos.Y);
GoToLineMenuItem.Visible := Pnl=0;
OpenFolderMenuItem.Visible := Pnl=4;
if Pnl in [0, 4] then
StatusPopUpMenu.PopUp;
i := StatusBar.GetPanelIndexAt(MousePos.X, MousePos.Y);
GoToLineMenuItem.Visible := i=0;
OpenFolderMenuItem.Visible := i=4;
if i in [0, 4] then
StatusPopUpMenu.PopUp
else
if i >= 0 then begin
pnl := FStatusPanels.GetItemForPanel(StatusBar.Panels[i]);
if (pnl <> nil) and (pnl.OnContextPopup <> nil) then
pnl.OnContextPopup(Self, MousePos, Handled);
end;
end;
procedure TSourceNotebook.StatusBarDrawPanel(AStatusBar: TStatusBar; APanel: TStatusPanel;
const ARect: TRect);
var
pnl: TSourceEditorStatusPanel;
begin
if APanel = StatusBar.Panels[1] then begin
IDEImages.Images_16.ResolutionForControl[16, AStatusBar]
.Draw(StatusBar.Canvas, ARect.Left, ARect.Top, FStopBtnIdx);
end
else
begin
pnl := FStatusPanels.GetItemForPanel(APanel);
if (pnl <> nil) and (pnl.OnDrawPanel <> nil) then
pnl.OnDrawPanel(pnl, StatusBar.Canvas, ARect);
end;
end;
@ -8467,6 +8720,11 @@ begin
Cursor:=crDefault;
end;
procedure TSourceNotebook.FormResize(Sender: TObject);
begin
CalculateStatusBarSizes;
end;
// Two Popup menu handlers:
procedure TSourceNotebook.GoToLineMenuItemClick(Sender: TObject);
begin
@ -8490,6 +8748,30 @@ begin
SourceEditorManager.ShowActiveWindowOnTop(True);
end;
procedure TSourceNotebook.CalculateStatusBarSizes;
var
i, W: Integer;
p: TSourceEditorStatusPanel;
begin
StatusBar.BeginUpdate;
try
for i := 0 to FStatusPanels.Count - 1 do begin
p := FStatusPanels[i];
if p.FVisible then
p.SetRealWidth(p.FWidth)
else
p.SetRealWidth(0);
end;
W := 0;
for i := 0 to StatusBar.Panels.Count - 1 do
if i <> 4 then
w := w + StatusBar.Panels[i].Width;
StatusBar.Panels[4].Width := Max(150, StatusBar.Width - W);
finally
StatusBar.EndUpdate;
end;
end;
procedure TSourceNotebook.ApplyPageIndex;
begin
Exclude(FUpdateFlags,ufPageIndexChanged);
@ -8760,6 +9042,46 @@ begin
CheckCurrentCodeBufferChanged;
End;
function TSourceNotebook.AddStatusPanel(AnOwner: TClass; ATag: PtrUInt
): TSourceEditorStatusPanelInterface;
var
R: TSourceEditorStatusPanel;
begin
R := TSourceEditorStatusPanel.Create(AnOwner, ATag);
Result := R;
R.FSrcNotebook := Self;
R.FPanel := StatusBar.Panels.Add;
R.FPanel.Width := 0;
R.Visible := False;
FStatusPanels.Add(R);
end;
function TSourceNotebook.StatusPanelCount(AnOwner: TClass): integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to FStatusPanels.Count - 1 do
if (FStatusPanels[i].Owner = AnOwner)
then
inc(Result);
end;
function TSourceNotebook.StatusPanelTaggedCount(AnOwner: TClass; ATag: PtrUInt
): integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to FStatusPanels.Count - 1 do
if (FStatusPanels[i].Owner = AnOwner) and
(FStatusPanels[i].Tag = ATag)
then
inc(Result);
end;
function TSourceNotebook.FindPageWithEditor(ASourceEditor: TSourceEditor): integer;
var
LTabSheet, LParent: TWinControl;