-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring - splitting up VirtualTrees.pas #1041
Comments
So I learned something about delphi doing this. When using type aliases unit VirtualTrees.Options;
type
TVTPaintOption = (
toHideFocusRect,
... unit VirtualTrees;
uses
VirtualTrees.Options;
type
TVTPaintOption = VirtualTrees.Options.TVTPaintOption; unit MyForm;
....
procedure TMyForm.FormCreate;
begin
TreeList.TreeOptions.PaintOptions := TreeList.TreeOptions.PaintOptions + [toHideFocusRect];
end; You see an error TMyForm.FormCreate - Undeclared identified 'toHideFocusRect' So I guess type aliases for enums are pretty useless. This makes it impossible to avoid breaking changes. In this case the breakage is just that the user need to add another unit to the uses clause, but I was hoping to avoid that as it's an inconvenience. Not sure if there is any other way around this. |
Maybe it is time to enforce scoped enums (also breaking change). |
I did think about that, and that does actually solve the type alias issue. |
There are plenty of other code style issues I'd love to attack as well. My pet hate, with statements using multiple identifiers e.g with Header, FixedAreaConstraints, TreeView do
begin makes the code very hard to understand and work with. But for now, I'm focusing on just splitting out code in to separate units. There's also a lot of drilling going on, ie column.columns.header.treeview.method sort of thing. Thats more of a design issue and not easily fixed. VT has grown too big, with too many options and the interaction between the options is hard to reason because the code is so hard to read. Styling and scaling are still issues. I have another scaling issue to look at that I discovered today (start the app on a higher dpi screen and then move it to lower, header doesn't scale correctly). Anyway, it's too big to rewrite, so all we can do is chip slowly away at it and try to improve it. It's still hands down the best tree control out there. |
oh yes ... Something about code formatting: I'd really love to see a Delphi formatter configuration checked in alongside the sources of as many Delphi projects as possible. I'm one of these guys who hits Ctrl+D more often than Ctrl+F9. |
While delphi allows you to save/load formatter settings, it's not like other editors that support editorconfig where you can ship the editorconfig file with the code and the editor will respect the settings. I typically have the delphi formatter disabled, as it really messes up generics. |
Delphi's code formatter will just not leave formatted comments alone, anyone know the right options to tell it to leave comments as is? |
I know, but the I can at least actively load the standard for the project.
Really? Probably it formats them consistently in a way that you are not used to... ;) We've got a company standard (mostly if-then-begin-end-linebreak stuff, the rest is pretty much default). Have neither looked at it nor touched it for years. Everyone is using generics and the formatter formats the code consistently. No "messing up" of generics observed. I also think that comments are left "in order". |
When I say messing up generics it was just a spacing issue.. I spent some time playing with the formatter today and managed to get it configured to roughly my liking. That said, it still messed with comments in ways I didn't like (for example the tabulated comments on the options enums). |
Well, I guess this will break a lot of code out there, while having only little benefit. Looking at the VCL or .Net TreeView It is also uncommon to use such base class types to reference the control. It seems more beneficial to me to extract the TVirtualDrawTree and TVirtualStringTree to own units.
I think this is acceptable. Especially if you extract an enum, const or a small type but add an alias to it, this does not really reduce the size of the unit. |
This is the result of running the delphi formatter with our settings over an enum with comments. "Another blank comment line" isn't touched by the formatter, so you have to adjust it manually if the lengths of the enum names change. Is this ok for you? |
Well I thought that would be the case - I use Virtual Treeview extensively and the only breaking change was with the enums. YMMV. The benefit for the users initially will be zero.. that was never my intention with this - the benefit will be easier maintenance of the code in the future. My use of type aliases was an attempt to avoid too many (even minor) breaking changes. I wonder how many people use the Header.TreeView property - if you have access to the header you likely also have access to the tree control. Same applies to Columns.Header prop. I commented both out, the code still compiles. Internally in the implementation section I used class helpers to avoid casting everywhere type
TBaseVirtualTreeCracker = class(TBaseVirtualTree);
TVTHeaderCracker = class(TVTHeader);
TVirtualTreeColumnHelper = class helper for TVirtualTreeColumn
function TreeView : TBaseVirtualTreeCracker;
function Header : TVTHeaderCracker;
end;
TVirtualTreeColumnsHelper = class helper for TVirtualTreeColumns
function Header : TVTHeaderCracker;
function TreeView : TBaseVirtualTreeCracker;
end;
{ TVirtualTreeColumnHelper }
function TVirtualTreeColumnHelper.Header: TVTHeaderCracker;
begin
result := TVTHeaderCracker(Owner.Header);
end;
function TVirtualTreeColumnHelper.TreeView: TBaseVirtualTreeCracker;
begin
result := TBaseVirtualTreeCracker(TVTHeaderCracker(Owner.Header).GetOwner);
end;
{ TVirtualTreeColumnsHelper }
function TVirtualTreeColumnsHelper.Header: TVTHeaderCracker;
begin
result := TVTHeaderCracker(FHeader);
end;
function TVirtualTreeColumnsHelper.TreeView: TBaseVirtualTreeCracker;
begin
result := TBaseVirtualTreeCracker(Header.GetOwner);
end;
@pyscripter suggestion to use scoped enums actually solves the type alias issue, in that you won't need to add another unit to the uses, just prefix the values with the enum type.. it's really 6 of one half a dozen of another and I think adding to the uses clause is less effort that fixing up every reference to enum values. I used scoped enums in my own code, but that's because I do a lot of c# work and it keeps it consistent between languages (easier on my brain) - but I wouldn't force it on the thousands of users of VT at this stage. As for extracting the TVirtualDrawTree and TVirtualStringTree to own units, absolutely.. I just didn't want to do too many changes in one go.. what I have done so far is already a lot.. (way more than I would usually do for a PR) but mostly it's just moving code from VirtualTrees.pas to new units and then fixing up access to protected and private members. |
Can you share your formatter settings? |
here you go. I appended .txt to make Github happy. |
Actually never mind, I found the setting, Align end of line comments. It kinda works ok, but still messes up in places TVTPaintOption = (toHideFocusRect, // Avoid drawing the dotted rectangle around the currently focused node.
toHideSelection, // Selected nodes are drawn as unselected nodes if the tree is unfocused.
toHotTrack, // Track which node is under the mouse cursor.
toFullVertGridLines, // Display vertical lines over the full client area, not only the space occupied by nodes.
// This option only has an effect if toShowVertGridLines is enabled too.
toAlwaysHideSelection, // Do not draw node selection, regardless of focused state.
toUseBlendedSelection, // Enable alpha blending for node selections. Gets changed to TVTPaintOption = (toHideFocusRect, // Avoid drawing the dotted rectangle around the currently focused node.
toHideSelection, // Selected nodes are drawn as unselected nodes if the tree is unfocused.
toHotTrack, // Track which node is under the mouse cursor.
toFullVertGridLines, // Display vertical lines over the full client area, not only the space occupied by nodes.
// This option only has an effect if toShowVertGridLines is enabled too.
toAlwaysHideSelection, // Do not draw node selection, regardless of focused state.
toUseBlendedSelection, // Enable alpha blending for node selections. I can cope with that.. I ran it on the new units I created but won't run it on the existing units. I'll add a formatter.config file with my PR. |
I guess it'll work fine if you put toHideFocusRect on a separate line. Edit: no you don't This is the unedited result with my settings. I just pasted your original and closed the enum:
|
Except the formatter puts it back on the first line. Looking for that option now (if it exists). |
Try it on the full TVTPaintOption as it is in VirtualTrees.pas |
Unedited. Just pressed Ctrl+D on VirtualTreevies.pas (Delphi 10.4.2) Maybe they have improved the Formatter...
|
I'm using 10.4.2 but see different results.. I will compare formatter settings and see what is causing the difference. |
We have a derived control class and some helper functions that use I still think extracting TVirtualDrawTree and TVirtualStringTree would be the better next step. |
Some observations:
For a complex project like TreeSize, that makes intense use of Virtual TreeView, the breaking changes are annoying. For the moment, I will give up after 40 minutes of compiler errors. I still think we are starting this from the wrong end and should begin with extracting TVirtualDrawTree and TVirtualStringTree. |
Points taken, I will move things around as suggested. As for extracting TVirtualDrawTree and TVirtualStringTree - I wanted to start with smaller breaking changes. I can do those too in this PR if you want, my thought was to do them in a follow up. |
I have pushed the changes and confirmed they build with XE3-10.4 |
I looked at extracting TVirtualDrawTree and TVirtualStringTree and it looks pretty simple, however we can't add alias's in VirtualTrees for them due to circular references - this would require that users update their uses clause. Let me know if you want me to do it in this PR. |
Thanks for making the recent changes. I reverted my TreeSize project and gave it another try. I found these chnages that could STRONGLY reduce the necessary changes:
|
I would definitely vote for extracting An option would be to make a major release V8.0 and include a few other breaking changes, especially #832. I don't think there is a benefit in having several releases of that each has smaller breaking changes. |
I have pushed the suggested changes, including splitting out TVirtualDrawTree. I didn't tackle #832 (although I think it's a good idea). |
Thank you! I found two cases that cause a breaking change beyond adding
|
I have pushed the change for the first one. Merging columns and headers seems wrong to me.. slowly heading back to one large file. Surely you can Type Cast where needed - or do what I did in the implementation section, create a helper for it - see TVirtualTreeColumnHelper in the columns unit. |
Copying around such helpers violates the DRY principle and so should not be part of a solution.
Both units have an interface section of roughly 250 lines. I don't think merging them would create a too large file. Having this split in two units does not create any benefit I can see. Delphi with its single pass compiler is not a good match with having super-small units, that produces large unit sections and/or the need of defining aliases all along. One needs to find a smooth point. |
I have merged columns into header and reverted the columns.header property type to TVTHeader. |
Thanks @vincentparrett for all the work, the pull request is now merged and breaking chnages are documented here: https://github.com/JAM-Software/Virtual-TreeView/wiki/Breaking-Changes-in-upcoming-V8 |
So now that most of the reliance on friend access has been remove, we can attempt to split things up to make the code base easier to work on.
Proposed split units (and where I am up to)
There will likely be a few more as I work through this.
Now a lot of the types are used in published props and event signatures, so what I am doing is adding aliases at the top of VirtualTrees.pas for those types - that will hopefully avoid breaking anyone's code (although the worst that would happen is they need to add to their uses clause).
There will be some (minor) breaking interface changes with this. For example TVTHeader exposes a TreeView property of type TBaseVirtualTree - that was only possible due to them being in the same unit.. with the split this becomes an issue of circular references. I changed this to TCustomControl and cast where needed in the code. There are a few more cases like this that couldn't be helped. I also needed to change the scope of some methods to public, I will try to avoid that using a cracker class where possible.
I'll update this issue with any breaking changes as I come across them. In the work I have done so far, VirtualTrees.pas shrunk from 38K to 27K lines - still way to large for lots of tooling (like github) but more manageable - and there is still more to split out.
Hopefully I should have a PR ready for review in a day or so.
The text was updated successfully, but these errors were encountered: