Attila Kovacs 629 Posted November 30, 2018 (edited) I was always pissed because of the property editor and its incapability to helping the work, so I made this extra filter-box. It has a little discomfort as the Property Editor steals the focus on the first click, I'll check what can I do if I find some time. Also, adding/deleting/storing the predefined filters are unimplemented yet. If someone feels the power to get it done, don't hesitate to apply. Edited November 30, 2018 by Attila Kovacs 5 Share this post Link to post
ertank 27 Posted November 30, 2018 Try this: Write some long text in a TLabel.Caption or a TEdit.Text when you have a vertical scrollbar visible in Object Inspector. Your cursor will go under that scrollbar and you will not see what you are typing beneath. Same applies when you try to change existing long text. Share this post Link to post
mael 29 Posted February 7, 2019 That's very useful! I always wanted to have an option that would keep certain properties "pinned", no matter what control is selected. Share this post Link to post
M.Joos 30 Posted February 7, 2019 Hi Attlia, looks very promising. But where can I find the code fort it ? Share this post Link to post
Attila Kovacs 629 Posted February 7, 2019 (edited) Thanks @mael and @M.Joos. This is actually very simple with the RegisterSelectionEditor. Here is the prototype I made, I don't really have any time to make it to a "product" nor to enhance it more. First you will need a simple wizard skeleton like http://www.tempest-sw.com/opentools/simple.html Then merge this code into it. Let me know if anything is missing. TCustomPropeFilter = class(TSelectionEditor, ISelectionpropertyFilter) private // I've no clue who is managing the instance so everything static class var FFilterPane: TPanel; class var FFilterBox: TButtonedEdit; class var FFilterCombo: TComboBox; class var FFilterList: TStringList; class var FPatternList: TStringList; class var FPropertyInspector: TForm; class var FOldPropertyInspectorActivate: TNotifyEvent; class procedure OnFilterComboCloseUp(Sender: TObject); class procedure RefreshSelection; class procedure OnFilterComboKeyPress(Sender: TObject; var Key: Char); class procedure OnPropertyInspectorActivate(Sender: TObject); protected { ISelectionPropertyFilter } procedure FilterProperties(const ASelection: IDesignerSelections; const ASelectionproperties: IInterfaceList); end; constructor TSimpleWizard.Create; begin // how to unregister? RegisterSelectionEditor(TControl, TCustomPropeFilter); TCustomPropeFilter.FPropertyInspector := TForm(Application.FindComponent('PropertyInspector')); if TCustomPropeFilter.FPropertyInspector <> nil then begin TCustomPropeFilter.FOldPropertyInspectorActivate := TCustomPropeFilter.FPropertyInspector.OnActivate; TCustomPropeFilter.FPropertyInspector.OnActivate := TCustomPropeFilter.OnPropertyInspectorActivate; TCustomPropeFilter.FFilterPane := TPanel(TCustomPropeFilter.FPropertyInspector.FindComponent('FilterPane')); if TCustomPropeFilter.FFilterPane <> nil then begin TCustomPropeFilter.FFilterBox := TButtonedEdit(TCustomPropeFilter.FPropertyInspector.FindComponent('FilterBox')); // I don't like the default search box. Do you? if TCustomPropeFilter.FFilterBox <> nil then TCustomPropeFilter.FFilterBox.Visible := False; TCustomPropeFilter.FFilterCombo := TComboBox.Create(TCustomPropeFilter.FFilterPane); TCustomPropeFilter.FFilterCombo.Name := 'FilterCombo'; TCustomPropeFilter.FFilterCombo.Text := ''; TCustomPropeFilter.FFilterCombo.Margins.Left := 0; TCustomPropeFilter.FFilterCombo.Margins.Top := 3; TCustomPropeFilter.FFilterCombo.Margins.Right := 0; TCustomPropeFilter.FFilterCombo.Margins.Bottom := 0; TCustomPropeFilter.FFilterCombo.AlignWithMargins := True; // TCustomPropeFilter.FFilterCombo.Top := 100; // needed only if default search-box is visible TCustomPropeFilter.FFilterCombo.Align := alTop; TCustomPropeFilter.FFilterPane.InsertControl(TCustomPropeFilter.FFilterCombo); TCustomPropeFilter.FFilterCombo.Items.Add('Pos & Size'); TCustomPropeFilter.FFilterCombo.Items.Add('Display'); TCustomPropeFilter.FFilterCombo.Items.Add('Name'); TCustomPropeFilter.FFilterCombo.Items.Add('NewForm'); TCustomPropeFilter.FFilterCombo.OnCloseUp := TCustomPropeFilter.OnFilterComboCloseUp; TCustomPropeFilter.FFilterCombo.OnKeyPress := TCustomPropeFilter.OnFilterComboKeyPress; TCustomPropeFilter.FFilterList := TStringList.Create; TCustomPropeFilter.FPatternList := TStringList.Create; end; end; end; { TCustomPropeFilter } class procedure TCustomPropeFilter.OnFilterComboCloseUp(Sender: TObject); begin RefreshSelection; end; class procedure TCustomPropeFilter.RefreshSelection; var AList: IDesignerSelections; Designer: IDesigner; begin Designer := ActiveRoot as IDesigner; if Designer = nil then Exit; AList := CreateSelectionList; Designer.GetSelections(AList); // If the selection contains only the current form. (No controls) if (AList.Count = 1) and (AList[0] = Designer.Root) then Designer.NoSelection else Designer.ClearSelection; Designer.SetSelections(AList); end; class procedure TCustomPropeFilter.OnFilterComboKeyPress(Sender: TObject; var Key: Char); begin case Key of #13: begin RefreshSelection; Key := #0; end; #27: begin if TCustomPropeFilter.FFilterCombo.Text <> '' then begin TCustomPropeFilter.FFilterCombo.ItemIndex := -1; TCustomPropeFilter.FFilterCombo.Text := ''; RefreshSelection; end; Key := #0; end; end; end; class procedure TCustomPropeFilter.OnPropertyInspectorActivate(Sender: TObject); var CursorPos: TPoint; c: TControl; begin // This is insane but I've no better idea to avoid stealing the focus by Property Editor's OnActivate event GetCursorPos(CursorPos); c := FFilterPane.ControlAtPos(FFilterPane.ScreenToClient(CursorPos), True, True, True); if (c <> nil) and (c.Name = 'FilterCombo') then Exit; if Assigned(FOldPropertyInspectorActivate) then FOldPropertyInspectorActivate(Sender); end; procedure TCustomPropeFilter.FilterProperties(const ASelection: IDesignerSelections; const ASelectionproperties: IInterfaceList); var i, j: Integer; LSelectedItem: TPersistent; LProperty: IProperty; LPropertyName: String; MethodProperty: IMethodProperty; function PatternMatch(AList: TStringList; APropName: String): Boolean; var k: Integer; begin for k := 0 to AList.Count - 1 do if Pos(AList[k], APropName) > 0 then Exit(True); Result := False; end; begin if TCustomPropeFilter.FFilterCombo <> nil then begin // todo load the list from file or registry case TCustomPropeFilter.FFilterCombo.ItemIndex of - 1: begin if TCustomPropeFilter.FFilterCombo.Text <> '' then begin TCustomPropeFilter.FPatternList.Clear; TCustomPropeFilter.FFilterList.Text := StringReplace(TCustomPropeFilter.FFilterCombo.Text, ',', #13#10, [rfReplaceAll]); for i := TCustomPropeFilter.FFilterList.Count - 1 to 0 do if Copy(TCustomPropeFilter.FFilterList[i], 1, 1) = '*' then begin TCustomPropeFilter.FPatternList.Add(Copy(TCustomPropeFilter.FFilterList[i].ToLower, 2)); TCustomPropeFilter.FFilterList.Delete(i); end; end else Exit; end; // everything hardcoded because I'm a lazy dog 0: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('Top'); TCustomPropeFilter.FFilterList.Add('Left'); TCustomPropeFilter.FFilterList.Add('Width'); TCustomPropeFilter.FFilterList.Add('Height'); TCustomPropeFilter.FFilterList.Add('ClientWidth'); TCustomPropeFilter.FFilterList.Add('ClientHeight'); TCustomPropeFilter.FFilterList.Add('TabOrder'); TCustomPropeFilter.FFilterList.Add('Align'); TCustomPropeFilter.FFilterList.Add('AlignWithMargins'); TCustomPropeFilter.FFilterList.Add('Margins'); end; 1: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('Caption'); TCustomPropeFilter.FFilterList.Add('Text'); TCustomPropeFilter.FFilterList.Add('Datasource'); TCustomPropeFilter.FFilterList.Add('DataField'); end; 2: begin TCustomPropeFilter.FFilterList.Text := 'Name'; end; 3: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('ActiveControl'); TCustomPropeFilter.FFilterList.Add('BorderIcons'); TCustomPropeFilter.FFilterList.Add('BorderStyle'); TCustomPropeFilter.FFilterList.Add('Caption'); TCustomPropeFilter.FFilterList.Add('ClientHeight'); TCustomPropeFilter.FFilterList.Add('ClientWidth'); TCustomPropeFilter.FFilterList.Add('Font'); TCustomPropeFilter.FFilterList.Add('Name'); TCustomPropeFilter.FFilterList.Add('Position'); end; end; // todo implement pattern search like in the original IDE search box for i := 0 to ASelection.Count - 1 do begin LSelectedItem := ASelection[i]; if (LSelectedItem is TControl) then for j := ASelectionproperties.Count - 1 downto 0 do begin if Supports(ASelectionproperties[j], IProperty, LProperty) then if not Supports(ASelectionproperties[j], IMethodProperty, MethodProperty) then begin LPropertyName := LProperty.GetName; if (TCustomPropeFilter.FFilterList.IndexOf(LPropertyName) = -1) and // (not PatternMatch(TCustomPropeFilter.FPatternList, LPropertyName.ToLower)) then ASelectionproperties.Delete(j); end; end; end; end; end; Edited February 7, 2019 by Attila Kovacs Share this post Link to post
M.Joos 30 Posted February 8, 2019 16 hours ago, Attila Kovacs said: Thanks @mael and @M.Joos. This is actually very simple with the RegisterSelectionEditor. Here is the prototype I made, I don't really have any time to make it to a "product" nor to enhance it more. First you will need a simple wizard skeleton like http://www.tempest-sw.com/opentools/simple.html Then merge this code into it. Let me know if anything is missing. TCustomPropeFilter = class(TSelectionEditor, ISelectionpropertyFilter) private // I've no clue who is managing the instance so everything static class var FFilterPane: TPanel; class var FFilterBox: TButtonedEdit; class var FFilterCombo: TComboBox; class var FFilterList: TStringList; class var FPatternList: TStringList; class var FPropertyInspector: TForm; class var FOldPropertyInspectorActivate: TNotifyEvent; class procedure OnFilterComboCloseUp(Sender: TObject); class procedure RefreshSelection; class procedure OnFilterComboKeyPress(Sender: TObject; var Key: Char); class procedure OnPropertyInspectorActivate(Sender: TObject); protected { ISelectionPropertyFilter } procedure FilterProperties(const ASelection: IDesignerSelections; const ASelectionproperties: IInterfaceList); end; constructor TSimpleWizard.Create; begin // how to unregister? RegisterSelectionEditor(TControl, TCustomPropeFilter); TCustomPropeFilter.FPropertyInspector := TForm(Application.FindComponent('PropertyInspector')); if TCustomPropeFilter.FPropertyInspector <> nil then begin TCustomPropeFilter.FOldPropertyInspectorActivate := TCustomPropeFilter.FPropertyInspector.OnActivate; TCustomPropeFilter.FPropertyInspector.OnActivate := TCustomPropeFilter.OnPropertyInspectorActivate; TCustomPropeFilter.FFilterPane := TPanel(TCustomPropeFilter.FPropertyInspector.FindComponent('FilterPane')); if TCustomPropeFilter.FFilterPane <> nil then begin TCustomPropeFilter.FFilterBox := TButtonedEdit(TCustomPropeFilter.FPropertyInspector.FindComponent('FilterBox')); // I don't like the default search box. Do you? if TCustomPropeFilter.FFilterBox <> nil then TCustomPropeFilter.FFilterBox.Visible := False; TCustomPropeFilter.FFilterCombo := TComboBox.Create(TCustomPropeFilter.FFilterPane); TCustomPropeFilter.FFilterCombo.Name := 'FilterCombo'; TCustomPropeFilter.FFilterCombo.Text := ''; TCustomPropeFilter.FFilterCombo.Margins.Left := 0; TCustomPropeFilter.FFilterCombo.Margins.Top := 3; TCustomPropeFilter.FFilterCombo.Margins.Right := 0; TCustomPropeFilter.FFilterCombo.Margins.Bottom := 0; TCustomPropeFilter.FFilterCombo.AlignWithMargins := True; // TCustomPropeFilter.FFilterCombo.Top := 100; // needed only if default search-box is visible TCustomPropeFilter.FFilterCombo.Align := alTop; TCustomPropeFilter.FFilterPane.InsertControl(TCustomPropeFilter.FFilterCombo); TCustomPropeFilter.FFilterCombo.Items.Add('Pos & Size'); TCustomPropeFilter.FFilterCombo.Items.Add('Display'); TCustomPropeFilter.FFilterCombo.Items.Add('Name'); TCustomPropeFilter.FFilterCombo.Items.Add('NewForm'); TCustomPropeFilter.FFilterCombo.OnCloseUp := TCustomPropeFilter.OnFilterComboCloseUp; TCustomPropeFilter.FFilterCombo.OnKeyPress := TCustomPropeFilter.OnFilterComboKeyPress; TCustomPropeFilter.FFilterList := TStringList.Create; TCustomPropeFilter.FPatternList := TStringList.Create; end; end; end; { TCustomPropeFilter } class procedure TCustomPropeFilter.OnFilterComboCloseUp(Sender: TObject); begin RefreshSelection; end; class procedure TCustomPropeFilter.RefreshSelection; var AList: IDesignerSelections; Designer: IDesigner; begin Designer := ActiveRoot as IDesigner; if Designer = nil then Exit; AList := CreateSelectionList; Designer.GetSelections(AList); // If the selection contains only the current form. (No controls) if (AList.Count = 1) and (AList[0] = Designer.Root) then Designer.NoSelection else Designer.ClearSelection; Designer.SetSelections(AList); end; class procedure TCustomPropeFilter.OnFilterComboKeyPress(Sender: TObject; var Key: Char); begin case Key of #13: begin RefreshSelection; Key := #0; end; #27: begin if TCustomPropeFilter.FFilterCombo.Text <> '' then begin TCustomPropeFilter.FFilterCombo.ItemIndex := -1; TCustomPropeFilter.FFilterCombo.Text := ''; RefreshSelection; end; Key := #0; end; end; end; class procedure TCustomPropeFilter.OnPropertyInspectorActivate(Sender: TObject); var CursorPos: TPoint; c: TControl; begin // This is insane but I've no better idea to avoid stealing the focus by Property Editor's OnActivate event GetCursorPos(CursorPos); c := FFilterPane.ControlAtPos(FFilterPane.ScreenToClient(CursorPos), True, True, True); if (c <> nil) and (c.Name = 'FilterCombo') then Exit; if Assigned(FOldPropertyInspectorActivate) then FOldPropertyInspectorActivate(Sender); end; procedure TCustomPropeFilter.FilterProperties(const ASelection: IDesignerSelections; const ASelectionproperties: IInterfaceList); var i, j: Integer; LSelectedItem: TPersistent; LProperty: IProperty; LPropertyName: String; MethodProperty: IMethodProperty; function PatternMatch(AList: TStringList; APropName: String): Boolean; var k: Integer; begin for k := 0 to AList.Count - 1 do if Pos(AList[k], APropName) > 0 then Exit(True); Result := False; end; begin if TCustomPropeFilter.FFilterCombo <> nil then begin // todo load the list from file or registry case TCustomPropeFilter.FFilterCombo.ItemIndex of - 1: begin if TCustomPropeFilter.FFilterCombo.Text <> '' then begin TCustomPropeFilter.FPatternList.Clear; TCustomPropeFilter.FFilterList.Text := StringReplace(TCustomPropeFilter.FFilterCombo.Text, ',', #13#10, [rfReplaceAll]); for i := TCustomPropeFilter.FFilterList.Count - 1 to 0 do if Copy(TCustomPropeFilter.FFilterList[i], 1, 1) = '*' then begin TCustomPropeFilter.FPatternList.Add(Copy(TCustomPropeFilter.FFilterList[i].ToLower, 2)); TCustomPropeFilter.FFilterList.Delete(i); end; end else Exit; end; // everything hardcoded because I'm a lazy dog 0: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('Top'); TCustomPropeFilter.FFilterList.Add('Left'); TCustomPropeFilter.FFilterList.Add('Width'); TCustomPropeFilter.FFilterList.Add('Height'); TCustomPropeFilter.FFilterList.Add('ClientWidth'); TCustomPropeFilter.FFilterList.Add('ClientHeight'); TCustomPropeFilter.FFilterList.Add('TabOrder'); TCustomPropeFilter.FFilterList.Add('Align'); TCustomPropeFilter.FFilterList.Add('AlignWithMargins'); TCustomPropeFilter.FFilterList.Add('Margins'); end; 1: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('Caption'); TCustomPropeFilter.FFilterList.Add('Text'); TCustomPropeFilter.FFilterList.Add('Datasource'); TCustomPropeFilter.FFilterList.Add('DataField'); end; 2: begin TCustomPropeFilter.FFilterList.Text := 'Name'; end; 3: begin TCustomPropeFilter.FFilterList.Clear; TCustomPropeFilter.FFilterList.Add('ActiveControl'); TCustomPropeFilter.FFilterList.Add('BorderIcons'); TCustomPropeFilter.FFilterList.Add('BorderStyle'); TCustomPropeFilter.FFilterList.Add('Caption'); TCustomPropeFilter.FFilterList.Add('ClientHeight'); TCustomPropeFilter.FFilterList.Add('ClientWidth'); TCustomPropeFilter.FFilterList.Add('Font'); TCustomPropeFilter.FFilterList.Add('Name'); TCustomPropeFilter.FFilterList.Add('Position'); end; end; // todo implement pattern search like in the original IDE search box for i := 0 to ASelection.Count - 1 do begin LSelectedItem := ASelection[i]; if (LSelectedItem is TControl) then for j := ASelectionproperties.Count - 1 downto 0 do begin if Supports(ASelectionproperties[j], IProperty, LProperty) then if not Supports(ASelectionproperties[j], IMethodProperty, MethodProperty) then begin LPropertyName := LProperty.GetName; if (TCustomPropeFilter.FFilterList.IndexOf(LPropertyName) = -1) and // (not PatternMatch(TCustomPropeFilter.FPatternList, LPropertyName.ToLower)) then ASelectionproperties.Delete(j); end; end; end; end; end; Thanks a lot. I will look and eventually extend your code somewhat. I am really missing the OI expert from Uwe Schuster, that went along similar ideas that you have. So maybe I can come up with a similar solution than Uwe's expert. At least your code is a good starting point, so thanks a lot for sharing. 1 Share this post Link to post
Tommi Prami 130 Posted February 13, 2019 Seems pretty cool. Would like to have some way to set this up, maybe an .ini-file etc, to give some custom properties. Did not check the code, so don't know that was it your plugin, but colorization of properties would alonne help a ton. Sometimes you spend very long time for property like Name etc. -Tee- Share this post Link to post
Tommi Prami 130 Posted February 13, 2019 (edited) I would like to see this plugin to get more support, now that code is already there, and installer, maybe, to speed up the start... Edit, how anyone can understand what I menat, coz I can't 😄 Would be nice to have installer for different IDE-versions, so it would be easier to start using it. I would suggest, if possible, add some standard colorizxation of properties, but it should be configurable, but editing INI-file would be OK, to me anyways. Because there are so many components and different properties, and some are interested in very different properties... -Tee- Edited February 14, 2019 by Tommi Prami 2 Share this post Link to post
Attila Kovacs 629 Posted February 16, 2019 (edited) @Tommi Prami Sorry, I'm not touching the property editor at all, so no coloring or any drawing possible. It has a filter interface where I can decide what should be in the list and what not. However, one could easily filter those properties which are declared only in the component class, or in the last 2 classes (TCustomXXX, but I think there are no published properties), or build a tree and select which ones you want to see. So could you have a clear overview of the components own properties. Edit: I made a little test and It's not that useful as I thought: (%'s represent the class depth (parents)) Edited February 16, 2019 by Attila Kovacs Share this post Link to post
Lars Fosdal 1792 Posted February 19, 2019 I think it could be fantastic if it was possible to design custom property lists per class. The first time a class is seen, it could be added to a list of class property configs, with all the props present. In a config dialog somewhere, it could be possible to enable/disable each prop. The filter could even use digits 0..9 to select one of multiple configurations per class. Naturally, if it could be installed with default filters for many of the standard components, that would be nice. I really like the idea - more than I like quick edit. Nice work! 1 Share this post Link to post
dummzeuch 1505 Posted February 19, 2019 (edited) On 2/7/2019 at 8:12 PM, Attila Kovacs said: This is actually very simple with the RegisterSelectionEditor. Here is the prototype I made, I don't really have any time to make it to a "product" nor to enhance it more. First you will need a simple wizard skeleton like http://www.tempest-sw.com/opentools/simple.html Then merge this code into it. Let me know if anything is missing. Where are TSelectionEditor and ISelectionpropertyFilter declared? I expected them in ToolsAPI, but they aren't there. Found them: DesignEditors and DesignIntf Edited February 19, 2019 by dummzeuch Share this post Link to post
Uwe Raabe 2057 Posted February 19, 2019 6 minutes ago, dummzeuch said: Where are TSelectionEditor and ISelectionpropertyFilter declared? I expected them in ToolsAPI, but they aren't there. DesignIntf and DesignEditors. BTW, the ImageIndexMapper makes use of these to replace the ImageIndex properties with the ImageName ones. Share this post Link to post
dummzeuch 1505 Posted February 19, 2019 Just in case anybody else wants to compile this code. It requires the following uses lists: interface uses Windows, SysUtils, Classes, ToolsAPI, ExtCtrls, StdCtrls, Forms, DesignEditors, ComponentDesigner, DesignIntf; implementation uses Controls, Types; 1 Share this post Link to post
Attila Kovacs 629 Posted February 19, 2019 @dummzeuch Sorry, I forgot them. As everybody every time... Btw. I just found out that it's not working if the selection is in a popup window like columns of a grid etc... I had no time yet to figure out what the problem is. Maybe you will have more ideas as me.. Share this post Link to post