rewrite confusing interface IEditableShortcut.

This commit is contained in:
Basile Burg 2020-03-08 21:38:05 +01:00
parent e335409c8f
commit 205c7bcfc1
6 changed files with 128 additions and 188 deletions

View File

@ -25,17 +25,17 @@ type
fNextPage: TShortCut; fNextPage: TShortCut;
fPrevPage: TShortCut; fPrevPage: TShortCut;
fDetectModuleName: boolean; fDetectModuleName: boolean;
fShCount: integer;
function optionedWantCategory(): string; function optionedWantCategory(): string;
function optionedWantEditorKind: TOptionEditorKind; function optionedWantEditorKind: TOptionEditorKind;
function optionedWantContainer: TPersistent; function optionedWantContainer: TPersistent;
procedure optionedEvent(event: TOptionEditorEvent); procedure optionedEvent(event: TOptionEditorEvent);
function optionedOptionsModified: boolean; function optionedOptionsModified: boolean;
//
function scedWantFirst: boolean; function scedCount: integer;
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function scedGetItem(const index: integer): TEditableShortcut;
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure scedSetItem(const index: integer; constref item: TEditableShortcut);
procedure scedSendDone;
published published
property pageButtons: TPageControlButtons read fPageButtons write fPageButtons; property pageButtons: TPageControlButtons read fPageButtons write fPageButtons;
property pageOptions: TPageControlOptions read fPageOptions write fPageOptions; property pageOptions: TPageControlOptions read fPageOptions write fPageOptions;
@ -280,38 +280,35 @@ begin
exit(false); exit(false);
end; end;
function TPagesOptions.scedWantFirst: boolean; function TPagesOptions.scedCount: integer;
begin begin
fShCount := 0; result := 4;
exit(true);
end; end;
function TPagesOptions.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function TPagesOptions.scedGetItem(const index: integer): TEditableShortcut;
begin begin
category := 'Editor pages'; result.category := 'Editor pages';
case fShCount of with result do
0: begin identifier := 'Select next page'; aShortcut:= fNextPage; end; case index of
1: begin identifier := 'Select previous page'; aShortcut:= fPrevPage; end; 0: begin identifier := 'Select next page'; shortcut:= fNextPage; end;
2: begin identifier := 'Move page left'; aShortcut:= fMoveLeft; end; 1: begin identifier := 'Select previous page'; shortcut:= fPrevPage; end;
3: begin identifier := 'Move page right'; aShortcut:= fMoveRight; end; 2: begin identifier := 'Move page left'; shortcut:= fMoveLeft; end;
end; 3: begin identifier := 'Move page right'; shortcut:= fMoveRight; end;
fShCount += 1; else raise Exception.CreateFmt(
result := fShCount <> 4; 'unexpected TPagesOptions editable shortcut index: %d', [index]);
end;
procedure TPagesOptions.scedSendItem(const category, identifier: string; aShortcut: TShortcut);
begin
case identifier of
'Select next page': fNextPage := aShortcut;
'Select previous page': fPrevPage := aShortcut;
'Move page left': fMoveLeft := aShortcut;
'Move page right': fMoveRight:= aShortcut;
end; end;
end; end;
procedure TPagesOptions.scedSendDone; procedure TPagesOptions.scedSetItem(const index: integer; constref item: TEditableShortcut);
begin begin
fShCount := 0; case index of
0: fNextPage := item.shortcut;
1: fPrevPage := item.shortcut;
2: fMoveLeft := item.shortcut;
3: fMoveRight:= item.shortcut;
else raise Exception.CreateFmt(
'unexpected TPagesOptions editable shortcut index: %d', [index]);
end;
end; end;
{$ENDREGION} {$ENDREGION}

View File

@ -148,24 +148,22 @@ type
TEditorOptions = class(TEditorOptionsBase, IEditableOptions, IDocumentObserver, IEditableShortCut) TEditorOptions = class(TEditorOptionsBase, IEditableOptions, IDocumentObserver, IEditableShortCut)
private private
fBackup: TEditorOptionsBase; fBackup: TEditorOptionsBase;
fShortcutCount: Integer;
//
function optionedWantCategory(): string; function optionedWantCategory(): string;
function optionedWantEditorKind: TOptionEditorKind; function optionedWantEditorKind: TOptionEditorKind;
function optionedWantContainer: TPersistent; function optionedWantContainer: TPersistent;
procedure optionedEvent(event: TOptionEditorEvent); procedure optionedEvent(event: TOptionEditorEvent);
function optionedOptionsModified: boolean; function optionedOptionsModified: boolean;
//
procedure docNew(document: TDexedMemo); procedure docNew(document: TDexedMemo);
procedure docFocused(document: TDexedMemo); procedure docFocused(document: TDexedMemo);
procedure docChanged(document: TDexedMemo); procedure docChanged(document: TDexedMemo);
procedure docClosing(document: TDexedMemo); procedure docClosing(document: TDexedMemo);
//
function scedWantFirst: boolean; function scedCount: integer;
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function scedGetItem(const index: integer): TEditableShortcut;
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure scedSetItem(const index: integer; constref item: TEditableShortcut);
procedure scedSendDone;
//
procedure applyChangeToEditor(anEditor: TDexedMemo); procedure applyChangeToEditor(anEditor: TDexedMemo);
protected protected
procedure afterLoad; override; procedure afterLoad; override;
@ -571,56 +569,31 @@ end;
{$ENDREGION} {$ENDREGION}
{$REGION IEditableShortCut ---------------------------------------------------} {$REGION IEditableShortCut ---------------------------------------------------}
function TEditorOptions.scedWantFirst: boolean; function TEditorOptions.scedCount: integer;
begin begin
result := fShortCuts.Count > 0; result := fShortCuts.Count;
fShortcutCount := 0;
end; end;
function TEditorOptions.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function TEditorOptions.scedGetItem(const index: integer): TEditableShortcut;
var var
shrct: TPersistentShortcut; s: TPersistentShortcut;
begin begin
shrct := TPersistentShortcut(fShortCuts.Items[fShortcutCount]); s := TPersistentShortcut(fShortCuts.Items[index]);
category := 'Code editor'; result.category := 'Code editor';
identifier:= shrct.actionName; result.identifier := s.actionName;
result.shortcut := s.shortcut;
// SynEdit shortcuts start with 'ec' // SynEdit shortcuts start with 'ec'
if identifier.length > 2 then if result.identifier.length > 2 then
identifier := identifier[3..identifier.length]; result.identifier := result.identifier[3..result.identifier.length];
aShortcut := shrct.shortcut;
fShortcutCount += 1;
result := fShortcutCount < fShortCuts.Count;
end; end;
procedure TEditorOptions.scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure TEditorOptions.scedSetItem(const index: integer; constref item: TEditableShortcut);
var var
i: Integer; s: TPersistentShortcut;
shc: TPersistentShortcut;
begin begin
if category <> 'Code editor' then s := TPersistentShortcut(fShortCuts.Items[index]);
exit; s.shortcut := item.shortcut;
for i:= 0 to fShortCuts.Count-1 do
begin
shc := TPersistentShortcut(fShortCuts.Items[i]);
if shc.actionName.length > 2 then
begin
if shc.actionName[3..shc.actionName.length] <> identifier then
continue;
end else if shc.actionName <> identifier then
continue;
shc.shortcut:= aShortcut;
break;
end;
// note: shortcut modifications are not reversible,
// they are sent from another option editor.
end; end;
procedure TEditorOptions.scedSendDone;
begin
applyChangesFromSelf;
end;
{$ENDREGION} {$ENDREGION}
{$REGION IEditableOptions ----------------------------------------------------} {$REGION IEditableOptions ----------------------------------------------------}

View File

@ -166,20 +166,26 @@ type
TProjectSubject = specialize TCustomSubject<IProjectObserver>; TProjectSubject = specialize TCustomSubject<IProjectObserver>;
(**
* Record used during communication between an IEditableShortCut and a TEditableShortCutSubject.
*)
TEditableShortcut = record
category: string;
identifier: string;
shortcut: TShortcut;
end;
(** (**
* An implementer can expose customizable shortcuts to be edited in a dedicated widget. * An implementer can expose customizable shortcuts to be edited in a dedicated widget.
*) *)
IEditableShortCut = interface(IObserverType) IEditableShortCut = interface(IObserverType)
['IEditableShortCut'] ['IEditableShortCut']
// a TEditableShortCutSubject will start to collect shortcuts if result. // a TEditableShortCutSubject requires the count of editable shortcuts.
function scedWantFirst: boolean; function scedCount: integer;
// a TEditableShortCutSubject collects the information on the shortcuts while result. // a TEditableShortCutSubject requires the nth editable shortcut.
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function scedGetItem(const index: integer): TEditableShortcut;
// a TEditableShortCutSubject sends the possibly modified shortcut. // a TEditableShortCutSubject send the shortcut with a new key binding.
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure scedSetItem(const index: integer; constref item: TEditableShortcut);
// a TEditableShortCutSubject has finished to send the shortcuts.
procedure scedSendDone;
end; end;
(** (**
* An implementer manages its observers shortcuts. * An implementer manages its observers shortcuts.

View File

@ -400,7 +400,6 @@ type
fFirstTimeRun: boolean; fFirstTimeRun: boolean;
fMultidoc: IMultiDocHandler; fMultidoc: IMultiDocHandler;
fProcInputHandler: IProcInputHandler; fProcInputHandler: IProcInputHandler;
fScCollectCount: Integer;
fUpdateCount: NativeInt; fUpdateCount: NativeInt;
fProject: ICommonProject; fProject: ICommonProject;
fFreeProj: ICommonProject; fFreeProj: ICommonProject;
@ -471,10 +470,9 @@ type
procedure projCompiled(project: ICommonProject; success: boolean); procedure projCompiled(project: ICommonProject; success: boolean);
// IEditableShortCut // IEditableShortCut
function scedWantFirst: boolean; function scedCount: integer;
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function scedGetItem(const index: integer): TEditableShortcut;
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure scedSetItem(const index: integer; constref item: TEditableShortcut);
procedure scedSendDone;
//Init - Fina //Init - Fina
procedure InitImages; procedure InitImages;
@ -2524,39 +2522,27 @@ end;
{$ENDREGION} {$ENDREGION}
{$REGION IEditableShortCut ---------------------------------------------------} {$REGION IEditableShortCut ---------------------------------------------------}
function TMainForm.scedWantFirst: boolean; function TMainForm.scedCount: integer;
begin begin
fScCollectCount := 0; result := actions.ActionCount;
result := true;
end; end;
function TMainForm.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function TMainForm.scedGetItem(const index: integer): TEditableShortcut;
var var
act: TCustomAction; a: TCustomAction;
begin begin
act := TCustomAction(Actions.Actions[fScCollectCount]); a := TCustomAction(Actions.Actions[index]);
category := act.Category; result.category := a.Category;
identifier := act.Caption; result.identifier := a.Caption;
aShortcut := act.ShortCut; result.shortcut := a.ShortCut;
fScCollectCount += 1;
result := fScCollectCount < actions.ActionCount;
end; end;
procedure TMainForm.scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure TMainForm.scedSetItem(const index: integer; constref item: TEditableShortcut);
var var
act: TCustomAction; a: TCustomAction;
i: integer;
begin
for i:= 0 to Actions.ActionCount-1 do
begin
act := TCustomAction(Actions.Actions[i]);
if (act.Category = category) and (act.Caption = identifier) then
act.ShortCut := aShortcut;
end;
end;
procedure TMainForm.scedSendDone;
begin begin
a := TCustomAction(Actions.Actions[index]);
a.ShortCut:= item.shortcut;
end; end;
{$ENDREGION} {$ENDREGION}

View File

@ -13,17 +13,19 @@ uses
type type
TShortcutItem = class(TCollectionItem) TShortcutItem = class(TCollectionItem)
private strict private
fIdentifier: string; fIdentifier: string;
fData: TShortcut; fData: TShortcut;
fDeclarator: IEditableShortCut; fDeclarator: IEditableShortCut;
property declarator: IEditableShortCut read fDeclarator write fDeclarator; fIndexInDecl: integer;
published published
property identifier: string read fIdentifier write fIdentifier; property identifier: string read fIdentifier write fIdentifier;
property data: TShortcut read fData write fData; property data: TShortcut read fData write fData;
public public
function combination: string; function combination: string;
procedure assign(source: TPersistent); override; procedure assign(source: TPersistent); override;
property declarator: IEditableShortCut read fDeclarator write fDeclarator;
property indexInDecl: integer read fIndexInDecl write fIndexInDecl;
end; end;
TShortCutCollection = class(TWritableLfmTextComponent) TShortCutCollection = class(TWritableLfmTextComponent)
@ -77,14 +79,14 @@ type
fHasChanged: boolean; fHasChanged: boolean;
propvalue: TEditableShortcut; propvalue: TEditableShortcut;
fHasScaled: boolean; fHasScaled: boolean;
//
procedure updateScaling; procedure updateScaling;
function optionedWantCategory(): string; function optionedWantCategory(): string;
function optionedWantEditorKind: TOptionEditorKind; function optionedWantEditorKind: TOptionEditorKind;
function optionedWantContainer: TPersistent; function optionedWantContainer: TPersistent;
procedure optionedEvent(event: TOptionEditorEvent); procedure optionedEvent(event: TOptionEditorEvent);
function optionedOptionsModified: boolean; function optionedOptionsModified: boolean;
//
function findCategory(const aName: string; aData: Pointer): TTreeNode; function findCategory(const aName: string; aData: Pointer): TTreeNode;
function findCategory(const aShortcutItem: TShortcutItem): string; function findCategory(const aShortcutItem: TShortcutItem): string;
function sortCategories(Cat1, Cat2: TTreeNode): integer; function sortCategories(Cat1, Cat2: TTreeNode): integer;
@ -92,6 +94,7 @@ type
procedure updateEditCtrls; procedure updateEditCtrls;
procedure sendShortcuts; procedure sendShortcuts;
function anItemIsSelected: boolean; function anItemIsSelected: boolean;
public public
constructor create(TheOwner: TComponent); override; constructor create(TheOwner: TComponent); override;
destructor destroy; override; destructor destroy; override;
@ -453,49 +456,43 @@ end;
procedure TShortcutEditor.receiveShortcuts; procedure TShortcutEditor.receiveShortcuts;
var var
i: Integer; i: integer;
obs: IEditableShortCut; j: integer;
cat: string; o: IEditableShortCut;
sht: word; s: TShortcutItem;
idt: string;
itm: TShortcutItem;
procedure addItem(); procedure addItem(constref item: u_interfaces.TEditableShortcut; const index: integer);
var var
prt: TTreeNode; prt: TTreeNode;
begin begin
// root category // root category
if cat.isEmpty or idt.isEmpty then if item.category.isEmpty or item.identifier.isEmpty then
exit; exit;
prt := findCategory(cat, obs); prt := findCategory(item.category, o);
if prt.isNil then if prt.isNil then
prt := tree.Items.AddObject(nil, cat, obs); prt := tree.Items.AddObject(nil, item.category, o);
// item as child // item as child
itm := TShortcutItem(fShortcuts.items.Add); s := TShortcutItem(fShortcuts.items.Add);
itm.identifier := idt; s.identifier := item.identifier;
itm.data:= sht; s.data := item.shortcut;
itm.declarator := obs; s.declarator := o;
tree.Items.AddChildObject(prt, idt, itm); s.indexInDecl := index;
cat := '';
idt := ''; tree.Items.AddChildObject(prt, item.identifier, s);
end; end;
begin begin
tree.Items.Clear; tree.Items.Clear;
fShortcuts.items.Clear; fShortcuts.items.Clear;
fBackup.items.Clear; fBackup.items.Clear;
cat := '';
idt := '';
for i:= 0 to fObservers.observersCount-1 do for i:= 0 to fObservers.observersCount-1 do
begin begin
obs := fObservers.observers[i] as IEditableShortCut; o := fObservers.observers[i] as IEditableShortCut;
if obs.scedWantFirst then for j:= 0 to o.scedCount-1 do
begin addItem(o.scedGetItem(j), j);
while obs.scedWantNext(cat, idt, sht) do
addItem();
addItem();
end;
end; end;
tree.Items.SortTopLevelNodes(@sortCategories); tree.Items.SortTopLevelNodes(@sortCategories);
fBackup.Assign(fShortcuts); fBackup.Assign(fShortcuts);
end; end;
@ -503,25 +500,22 @@ end;
procedure TShortcutEditor.sendShortcuts; procedure TShortcutEditor.sendShortcuts;
var var
i: integer; i: integer;
shc: TShortcutItem; s: TShortcutItem;
decl: IEditableShortCut = nil; d: IEditableShortCut = nil;
cat: string; c: string;
n: u_interfaces.TEditableShortcut;
begin begin
for i := 0 to fShortcuts.count-1 do for i := 0 to fShortcuts.count-1 do
begin begin
shc := fShortcuts[i]; s := fShortcuts[i];
decl:= shc.declarator; d := s.declarator;
if decl = nil then c := findCategory(s);
if not assigned(d) or c.isEmpty() then
continue; continue;
cat := findCategory(shc); n.identifier:= s.identifier;
if cat.isEmpty then n.category := c;
continue; n.shortcut := s.data;
decl.scedSendItem(cat, shc.identifier, shc.data); d.scedSetItem(s.indexInDecl, n);
if i = fShortcuts.count-1 then
decl.scedSendDone
// fShortcuts is always sorted by declarator, cf. receiveShortcuts()
else if decl <> fShortcuts[i+1].declarator then
decl.scedSendDone;
end; end;
end; end;
{$ENDREGION} {$ENDREGION}

View File

@ -108,10 +108,10 @@ type
procedure projCompiling(project: ICommonProject); procedure projCompiling(project: ICommonProject);
procedure projCompiled(project: ICommonProject; success: boolean); procedure projCompiled(project: ICommonProject; success: boolean);
function scedWantFirst: boolean; function scedCount: integer;
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function scedGetItem(const index: integer): TEditableShortcut;
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure scedSetItem(const index: integer; constref item: TEditableShortcut);
procedure scedSendDone;
published published
property tools: TToolItems read fTools write setTools; property tools: TToolItems read fTools write setTools;
property readOnly: boolean read fReadOnly write fReadOnly; property readOnly: boolean read fReadOnly write fReadOnly;
@ -375,37 +375,21 @@ end;
{$ENDREGION} {$ENDREGION}
{$REGION IEditableShortCut -----------------------------------------------------} {$REGION IEditableShortCut -----------------------------------------------------}
function TTools.scedWantFirst: boolean; function TTools.scedCount: integer;
begin begin
result := fTools.Count > 0; result := fTools.Count;
fShctCount := 0;
end; end;
function TTools.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean; function TTools.scedGetItem(const index: integer): TEditableShortcut;
begin begin
category := 'Tools'; result.category := 'Tools';
identifier:= tool[fShctCount].toolAlias; result.identifier := tool[index].toolAlias;
aShortcut := tool[fShctCount].shortcut; result.shortcut := tool[index].shortcut;
fShctCount += 1;
result := fShctCount < fTools.Count;
end; end;
procedure TTools.scedSendItem(const category, identifier: string; aShortcut: TShortcut); procedure TTools.scedSetItem(const index: integer; constref item: TEditableShortcut);
var
i: Integer;
begin
if category <> 'Tools' then
exit;
for i := 0 to tools.Count-1 do if tool[i].toolAlias = identifier then
begin
tool[i].shortcut := aShortcut;
break;
end;
end;
procedure TTools.scedSendDone;
begin begin
tool[index].shortcut := item.shortcut;
end; end;
{$ENDREGION} {$ENDREGION}