* Minimap initial implementation

This commit is contained in:
Michaël Van Canneyt 2024-09-26 21:40:27 +02:00
parent 744004e687
commit 0677da7644
8 changed files with 734 additions and 0 deletions

View File

@ -0,0 +1,22 @@
# Lazarus code editor mini map
This directory contains a mini map:
A small version of a source code window, which can be used to navigate an
overview of the source code.
The map is somewhat configurable (see the Tools-Options dialog):
- Map can be enabled/Disabled
- The width can be set
- The color of the window indicator
- The color of the window indicator text
- The initial size of the text can be set.
You can click in the mini map and the source will jump to the clicked
location
Using ctrl-scroll+mouse wheel will enlarge/shrink the mini map text.
This control is a complete re-implementation based on an idea by
Domingo Galmés, see his plugin:
https://github.com/DomingoGP/lazIdeMiniMap

View File

@ -0,0 +1,220 @@
{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Abstract:
Mini map controller
}
unit CtrlMiniMap;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Graphics, IDEOptEditorIntf, SrcEditorIntf, LazConfigStorage, pnlMiniMap;
const
DefaultEnabled = True;
type
{ TMinimapController }
TMinimapController = Class(TComponent)
private
FConfigFrame: TAbstractIDEOptionsEditorClass;
FList: TFPList;
FEnabled: Boolean;
FNeedSave : Boolean;
FInitialViewFontSize: Integer;
FMapWidth: Integer;
FViewWindowColor: TColor;
FViewWindowTextColor: TColor;
procedure SetEnabled(AValue: Boolean);
procedure SetInitialViewFontSize(AValue: Integer);
procedure SetMapWidth(AValue: Integer);
procedure SetViewWindowColor(AValue: TColor);
procedure SetViewWindowTextColor(AValue: TColor);
protected
procedure NewEditorCreated(Sender: TObject);
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ConfigPanel(aPanel: TMiniMapControl; aFull: Boolean = False);
Public
Constructor Create(aOwner : TComponent); override;
Destructor Destroy; override;
Procedure LoadConfig;
procedure SaveConfig;
function ShowConfig: Boolean;
Procedure ReconfigurePanels;
Property Enabled : Boolean Read FEnabled Write SetEnabled;
Property MapWidth : Integer Read FMapWidth Write SetMapWidth;
Property InitialViewFontSize : Integer Read FInitialViewFontSize Write SetInitialViewFontSize;
Property ViewWindowColor : TColor Read FViewWindowColor Write SetViewWindowColor;
Property ViewWindowTextColor : TColor Read FViewWindowTextColor Write SetViewWindowTextColor;
Property ConfigFrame : TAbstractIDEOptionsEditorClass Read FConfigFrame Write FConfigFrame;
end;
Var
MinimapController : TMinimapController;
implementation
uses Controls, ExtCtrls, Forms, LazIDEIntf, BaseIDEIntf, strMiniMap;
{ TMinimapController }
procedure TMinimapController.SetEnabled(AValue: Boolean);
begin
if FEnabled=AValue then Exit;
FEnabled:=AValue;
FNeedSave:=True;
if SourceEditorManagerIntf <> nil then
if AValue then
SourceEditorManagerIntf.RegisterChangeEvent(semEditorCreate,@NewEditorCreated)
else
SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorCreate,@NewEditorCreated);
end;
procedure TMinimapController.SetInitialViewFontSize(AValue: Integer);
begin
if FInitialViewFontSize=AValue then Exit;
FInitialViewFontSize:=AValue;
FNeedSave:=True;
end;
procedure TMinimapController.SetMapWidth(AValue: Integer);
begin
if FMapWidth=AValue then Exit;
FMapWidth:=AValue;
FNeedSave:=True;
end;
procedure TMinimapController.SetViewWindowColor(AValue: TColor);
begin
if FViewWindowColor=AValue then Exit;
FViewWindowColor:=AValue;
FNeedSave:=True;
end;
procedure TMinimapController.SetViewWindowTextColor(AValue: TColor);
begin
if FViewWindowTextColor=AValue then Exit;
FViewWindowTextColor:=AValue;
FNeedSave:=True;
end;
procedure TMinimapController.NewEditorCreated(Sender: TObject);
var
Editor : TSourceEditorInterface absolute Sender;
EditorWindow : TSourceEditorWindowInterface;
Panel : TMiniMapControl;
begin
EditorWindow:=SourceEditorManagerIntf.SourceWindowWithEditor(Editor);
Panel:=TMiniMapControl.Create(EditorWindow);
FList.Add(Panel);
Panel.FreeNotification(Self);
Panel.SourceEditor:=Editor;
ConfigPanel(Panel,True);
EditorWindow.AddControlToEditor(Editor,Panel,alRight);
end;
procedure TMinimapController.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation=opRemove) and (aComponent is TMiniMapControl) then
FList.Remove(AComponent);
end;
procedure TMinimapController.ConfigPanel(aPanel: TMiniMapControl; aFull : Boolean);
begin
aPanel.Width:=MapWidth;
aPanel.ViewWindowColor:=ViewWindowColor;
aPanel.ViewWindowTextColor:=ViewWindowTextColor;
If aFull then
aPanel.ViewFontSize:=InitialViewFontSize;
end;
constructor TMinimapController.Create(aOwner: TComponent);
begin
Inherited;
FList:=TFPList.Create;
FMapWidth:=DefaultMapWidth;
FInitialViewFontSize:=DefaultViewFontSize;
FViewWindowColor:=DefaultViewWindowColor;
FViewWindowTextColor:=DefaultViewWindowTextColor;
Enabled:=True;
end;
destructor TMinimapController.Destroy;
begin
if FNeedSave then
SaveConfig;
FreeAndNil(FList);
inherited Destroy;
end;
procedure TMinimapController.LoadConfig;
var
Storage : TConfigStorage;
begin
Storage:=GetIDEConfigStorage(SConfigFile, True);
with Storage do
try
Enabled:=GetValue(KeyEnabled,DefaultEnabled);
MapWidth:=GetValue(KeyWidth,DefaultMapWidth);
ViewWindowColor:=GetValue(KeyViewWindowColor,DefaultViewWindowColor);
ViewWindowTextColor:=GetValue(KeyViewWindowTextColor,DefaultViewWindowTextColor);
InitialViewFontSize:=GetValue(KeyInitialFontSize,DefaultViewFontSize);
FNeedSave := False;
finally
Free;
end;
end;
procedure TMinimapController.SaveConfig;
var
Storage : TConfigStorage;
begin
Storage:=GetIDEConfigStorage(SConfigFile, True);
with Storage do
try
SetDeleteValue(KeyEnabled,Enabled,DefaultEnabled);
SetDeleteValue(KeyWidth,MapWidth,DefaultMapWidth);
SetDeleteValue(KeyViewWindowColor,ViewWindowColor,DefaultViewWindowColor);
SetDeleteValue(KeyViewWindowTextColor,ViewWindowTextColor,DefaultViewWindowTextColor);
SetDeleteValue(KeyInitialFontSize,InitialViewFontSize,DefaultViewFontSize);
FNeedSave := False;
finally
Free;
end;
end;
function TMinimapController.ShowConfig: Boolean;
begin
Result:=LazarusIDE.DoOpenIDEOptions(ConfigFrame);
if Result then
ReconfigurePanels;
end;
procedure TMinimapController.ReconfigurePanels;
var
I : Integer;
begin
Writeln('ReconfigurePanels');
For I:=0 to FList.Count-1 do
ConfigPanel(TMiniMapControl(Flist[i]),False);
end;
end.

View File

@ -0,0 +1,97 @@
{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Abstract:
Options dialog for minimap
}
unit fraMiniMapConfig;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, StdCtrls, ColorBox, Dialogs, SpinEx,
IDEOptionsIntf, IDEOptEditorIntf, IDEUtils, IDEDialogs;
type
{ TMiniMapConfigFrame }
TMiniMapConfigFrame = class(TAbstractIDEOptionsEditor)
cbEnabled: TCheckBox;
cbViewWindow: TColorBox;
cbViewText: TColorBox;
CDView: TColorDialog;
lblViewWindowColor: TLabel;
lblViewWindowTextColor: TLabel;
lblMapWidth: TLabel;
lblInitialFontSize: TLabel;
seWidth: TSpinEditEx;
seInitialFontSize: TSpinEditEx;
private
public
function GetTitle: String; override;
procedure Setup({%H-}ADialog: TAbstractOptionsEditorDialog); override;
procedure ReadSettings({%H-}AOptions: TAbstractIDEOptions); override;
procedure WriteSettings({%H-}AOptions: TAbstractIDEOptions); override;
class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
end;
implementation
uses CtrlMiniMap, StrMiniMap;
{$R *.lfm}
{ TMiniMapConfigFrame }
function TMiniMapConfigFrame.GetTitle: String;
begin
Result:=SMinimapConfigTitle
end;
procedure TMiniMapConfigFrame.Setup(ADialog: TAbstractOptionsEditorDialog);
begin
// nothing
end;
procedure TMiniMapConfigFrame.ReadSettings(AOptions: TAbstractIDEOptions);
var
C : TMiniMapController;
begin
C:=MiniMapController;
cbEnabled.Checked:=C.Enabled;
seWidth.Value:=C.MapWidth;
seInitialFontSize.Value:=C.InitialViewFontSize;
cbViewWindow.Selected:=C.ViewWindowColor;
cbViewText.Selected:=C.ViewWindowTextColor;
end;
procedure TMiniMapConfigFrame.WriteSettings(AOptions: TAbstractIDEOptions);
var
C : TMiniMapController;
begin
C:=MiniMapController;
C.Enabled:=cbEnabled.Checked;
C.MapWidth:=seWidth.Value;
C.InitialViewFontSize:=seInitialFontSize.Value;
C.ViewWindowColor:=cbViewWindow.Selected;
C.ViewWindowTextColor:=cbViewText.Selected;
C.SaveConfig;
C.ReconfigurePanels;
end;
class function TMiniMapConfigFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
begin
Result:=IDEEditorGroups.GetByIndex(GroupEnvironment)^.GroupClass;
end;
end.

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<Package Version="5">
<Name Value="lazminimap"/>
<Type Value="DesignTime"/>
<Author Value="Michael Van Canneyt"/>
<CompilerOptions>
<Version Value="11"/>
<SearchPaths>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
</CompilerOptions>
<Description Value="Mini Map of source code editor.
"/>
<License Value="Standard Lazarus Modified LGPL"/>
<Version Major="1"/>
<Files>
<Item>
<Filename Value="reglazminimap.pas"/>
<HasRegisterProc Value="True"/>
<UnitName Value="RegLazMiniMap"/>
</Item>
<Item>
<Filename Value="pnlminimap.pas"/>
<UnitName Value="PnlMiniMap"/>
</Item>
<Item>
<Filename Value="framinimapconfig.pas"/>
<UnitName Value="FraMiniMapConfig"/>
</Item>
<Item>
<Filename Value="strminimap.pas"/>
<UnitName Value="StrMiniMap"/>
</Item>
<Item>
<Filename Value="ctrlminimap.pas"/>
<UnitName Value="CtrlMiniMap"/>
</Item>
</Files>
<RequiredPkgs>
<Item>
<PackageName Value="LazControlDsgn"/>
</Item>
<Item>
<PackageName Value="LCL"/>
</Item>
<Item>
<PackageName Value="SynEdit"/>
</Item>
<Item>
<PackageName Value="IDEIntf"/>
</Item>
<Item>
<PackageName Value="FCL"/>
</Item>
</RequiredPkgs>
<UsageOptions>
<UnitPath Value="$(PkgOutDir)"/>
</UsageOptions>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
</Package>
</CONFIG>

View File

@ -0,0 +1,23 @@
{ This file was automatically created by Lazarus. Do not edit!
This source is only used to compile and install the package.
}
unit lazminimap;
{$warn 5023 off : no warning about unused units}
interface
uses
reglazminimap, pnlminimap, framinimapconfig, strminimap, ctrlminimap,
LazarusPackageIntf;
implementation
procedure Register;
begin
RegisterUnit('reglazminimap', @reglazminimap.Register);
end;
initialization
RegisterPackage('lazminimap', @Register);
end.

View File

@ -0,0 +1,252 @@
{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Abstract:
Mini-Map panel control
}
unit PnlMiniMap;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Controls, ExtCtrls, SynEdit, SrcEditorIntf, Graphics,
SynEditMarkupSpecialLine, SynEditTypes, SynEditMiscClasses, SynEditMarkupBracket;
Const
DefaultViewFontSize = 3;
DefaultViewWindowColor = TColor($00E3F33F);
DefaultViewWindowTextColor = clNone;
DefaultMapWidth = 200;
Type
{ TMiniMapControl }
TMiniMapControl = Class(TPanel)
Private
FMiniSynEdit: TSynEdit;
FSourceEditor: TSourceEditorInterface;
FSourceSynEdit: TCustomSynEdit;
FViewWindowColor:TColor;
FViewWindowTextColor:TColor;
FViewFontSize: Integer;
procedure ConfigMiniEdit;
procedure SetSourceEditor(AValue: TSourceEditorInterface);
procedure SetViewFontSize(AValue: Integer);
procedure SetViewWindowColor(AValue: TColor);
procedure SetViewWindowTextColor(AValue: TColor);
procedure SyncMiniMapProps;
procedure UnHook;
Protected
// Event handlers
procedure HandleLineMarkup(Sender: TObject; Line: integer; var Special: boolean; Markup: TSynSelectedColor); virtual;
procedure HandleStatusChange(Sender: TObject; Changes: TSynStatusChanges); virtual;
Procedure HandleClick(aSender : TObject); virtual;
procedure HandleEditorDestroy(Sender: TObject); virtual;
Procedure SyncViewWindow;
Property MiniSynEdit : TSynEdit Read FMiniSynEdit;
Property SourceSynEdit : TCustomSynEdit Read FSourceSynEdit;
Public
constructor create(aOwner : TComponent); override;
destructor destroy; override;
Property SourceEditor: TSourceEditorInterface Read FSourceEditor Write SetSourceEditor;
Property ViewWindowColor : TColor Read FViewWindowColor Write SetViewWindowColor;
Property ViewWindowTextColor:TColor Read FViewWindowTextColor Write SetViewWindowTextColor;
Property ViewFontSize : Integer Read FViewFontSize Write SetViewFontSize;
end;
implementation
{ TMiniMapControl }
procedure TMiniMapControl.HandleLineMarkup(Sender: TObject; Line: integer;
var Special: boolean; Markup: TSynSelectedColor);
var
TopLine,BottomLine: Integer;
begin
TopLine:=FSourceSynEdit.TopLine;
BottomLine:=TopLine+FSourceSynEdit.LinesInWindow;
if (Line>=TopLine) and (Line<=BottomLine) then
begin
Markup.Background:=FViewWindowColor;
Markup.Foreground:=FViewWindowTextColor;
Special:=True;
end;
end;
procedure TMiniMapControl.HandleStatusChange(Sender: TObject;
Changes: TSynStatusChanges);
begin
SyncViewWindow;
end;
procedure TMiniMapControl.SetSourceEditor(AValue: TSourceEditorInterface);
begin
if FSourceEditor=AValue then Exit;
FSourceEditor:=AValue;
FSourceSynEdit:=TCustomSynEdit(FSourceEditor.EditorControl);
if Assigned(FSourceSynEdit) then
begin
SyncMiniMapProps;
SyncViewWindow;
end;
end;
procedure TMiniMapControl.SetViewFontSize(AValue: Integer);
begin
if ViewFontSize=AValue then Exit;
FViewFontSize:=AValue;
FMiniSynEdit.Font.Size:=FViewFontSize;
end;
procedure TMiniMapControl.SetViewWindowColor(AValue: TColor);
begin
if FViewWindowColor=AValue then Exit;
FViewWindowColor:=AValue;
FMiniSynEdit.Invalidate;
end;
procedure TMiniMapControl.SetViewWindowTextColor(AValue: TColor);
begin
if FViewWindowTextColor=AValue then Exit;
FViewWindowTextColor:=AValue;
FMiniSynEdit.Invalidate;
end;
procedure TMiniMapControl.HandleClick(aSender: TObject);
begin
SourceSynEdit.TopLine:=FMiniSynEdit.CaretY-(FSourceSynEdit.LinesInWindow div 2); //centered.
SyncViewWindow;
end;
procedure TMiniMapControl.SyncViewWindow;
var
CurrTop,CurrBottom : Integer;
begin
if (FSourceSynEdit=nil) then
Exit;
CurrTop:=FSourceSynEdit.TopLine;
CurrBottom:=CurrTop+FSourceSynEdit.LinesInWindow;
if (CurrTop<FMiniSynEdit.TopLine) then
FMiniSynEdit.TopLine:=CurrTop
else if (CurrBottom>(FMiniSynEdit.TopLine+FMiniSynEdit.LinesInWindow)) then
FMiniSynEdit.TopLine:=CurrBottom-FMiniSynEdit.LinesInWindow;
FMiniSynEdit.Invalidate;
end;
procedure TMiniMapControl.SyncMiniMapProps;
begin
With FMiniSynEdit do
begin
Font:=FSourceSynEdit.Font;
Font.Size:=FViewFontSize;
ShareTextBufferFrom(FSourceSynEdit);
Highlighter:=FSourceSynEdit.Highlighter;
RightEdge:=FSourceSynEdit.RightEdge;
RightEdgeColor:=FSourceSynEdit.RightEdgeColor;
Color:=FSourceSynEdit.Color;
FSourceSynEdit.RegisterStatusChangedHandler(@HandleStatusChange,[scTopLine, scLinesInWindow]);
end;
end;
procedure TMiniMapControl.ConfigMiniEdit;
var
I : integer;
begin
With FMiniSynEdit do
begin
Left:=0;
Top:=0;
Align:=alClient;
ParentColor:=False;
ParentFont:=False;
Font.Name := 'Courier New';
Font.Pitch := fpFixed;
Font.Quality := fqNonAntialiased;
Gutter.Visible := False;
Gutter.Width := 57;
RightGutter.Width := 0;
VisibleSpecialChars := [vscSpace, vscTabAtLast] ;
SelectedColor.BackPriority := 50;
SelectedColor.ForePriority := 50;
SelectedColor.FramePriority := 50;
SelectedColor.BoldPriority := 50;
SelectedColor.ItalicPriority := 50;
SelectedColor.UnderlinePriority := 50;
SelectedColor.StrikeOutPriority := 50;
BracketHighlightStyle := sbhsBoth;
BracketMatchColor.Background := clNone;
BracketMatchColor.Foreground := clNone;
BracketMatchColor.Style := [fsBold];
FoldedCodeColor.Background := clNone;
FoldedCodeColor.Foreground := clGray;
FoldedCodeColor.FrameColor := clGray;
MouseLinkColor.Background := clNone;
MouseLinkColor.Foreground := clBlue;
LineHighlightColor.Background := clNone;
LineHighlightColor.Foreground := clNone;
end;
SourceEditorManagerIntf.GetEditorControlSettings(FMiniSynEdit);
FMiniSynEdit.Font.Size:=FViewFontSize;
FMiniSynEdit.ReadOnly := True;
FMiniSynEdit.Gutter.Visible := False;
FMiniSynEdit.OnClick := @HandleClick;
FMiniSynEdit.OnSpecialLineMarkup := @HandleLineMarkup;
For I:=0 to FMiniSynEdit.Gutter.Parts.Count-1 do
FMiniSynEdit.Gutter.Parts[I].Visible:=True;
// FMiniSynEdit.Gutter.Parts[4].Visible := False; // code folding disabled.
end;
procedure TMiniMapControl.HandleEditorDestroy(Sender : TObject);
begin
if (Sender=FSourceEditor) then
Unhook;
end;
constructor TMiniMapControl.create(aOwner: TComponent);
begin
Inherited;
BevelInner:=bvNone;
BevelOuter:=bvNone;
FMiniSynEdit:=TSynEdit.Create(Self);
FMiniSynEdit.Parent:=Self;
FViewFontSize:=DefaultViewFontSize;
FViewWindowColor:=DefaultViewWindowColor;
FViewWindowTextColor:=DefaultViewWindowTextColor;
SourceEditorManagerIntf.RegisterChangeEvent(semEditorDestroy, @HandleEditorDestroy);
ConfigMiniEdit;
end;
procedure TMiniMapControl.UnHook;
begin
if Not Assigned(FSourceSynEdit) then
exit;
FMiniSynedit.UnShareTextBuffer;
FSourceSynEdit.UnRegisterStatusChangedHandler(@HandleStatusChange);
FSourceSynEdit:=nil;
FSourceEditor:=nil;
end;
destructor TMiniMapControl.destroy;
begin
Unhook;
inherited destroy;
end;
end.

View File

@ -0,0 +1,32 @@
unit RegLazMiniMap;
{$mode objfpc}{$H+}
interface
uses
CtrlMiniMap, Forms;
procedure register;
implementation
uses fraMiniMapConfig, IDEOptionsIntf, IDEOptEditorIntf;
var
MiniMapOptionsFrameID : integer = 2100;
procedure register;
begin
MiniMapController:=TMinimapController.Create(Application);
MiniMapController.ConfigFrame:=TMiniMapConfigFrame;
MiniMapController.LoadConfig;
// add IDE options frame
MiniMapOptionsFrameID:=RegisterIDEOptionsEditor(GroupEnvironment,TMiniMapConfigFrame,
MiniMapOptionsFrameID)^.Index;
end;
end.

View File

@ -0,0 +1,23 @@
unit StrMiniMap;
{$mode objfpc}{$H+}
interface
const
SConfigFile = 'minimap.xml';
KeyEnabled = 'Enabled';
KeyWidth = 'Width';
KeyViewWindowColor = 'ViewWindowColor';
KeyViewWindowTextColor = 'ViewWindowTextColor';
KeyInitialFontSize = 'InitialFontSize';
Resourcestring
SMinimapConfigTitle = 'MiniMap';
implementation
end.