elements icon indicating copy to clipboard operation
elements copied to clipboard

browse for file/directory widget

Open Xeverous opened this issue 5 years ago • 40 comments

This issue is for tracking any decisions before implementing this widget.

  • likely just a button with predefined on_click function
  • requires platform-specific code

Xeverous avatar Jan 22 '20 14:01 Xeverous

This is best done using platform code; Probably outside the scope of the library? I'm not sure.

djowel avatar Jan 22 '20 14:01 djowel

Not sure about other systems, but Windows has a set of predefined modal dialogs

https://docs.microsoft.com/en-us/windows/win32/dlgbox/dialog-box-types

You just call an API function and it creates a modal window managed by the OS, the function will return (with user data) when the window is closed.

Xeverous avatar Jan 22 '20 14:01 Xeverous

All platforms have those. It might be good to abstract this in a cross-platform API, but I am not sure where to draw the line which elements should support. How this is triggered is App specific. It can be from a menu, a button, etc.

djowel avatar Jan 22 '20 14:01 djowel

True, I was thinking more about providing such platform-abstracted API so one can easily create a custom widget and do something like w.on_click = os_modal_save_as().

Xeverous avatar Jan 22 '20 14:01 Xeverous

True, I was thinking more about providing such platform-abstracted API so one can easily create a custom widget and do something like w.on_click = os_modal_save_as().

Agreed. Probably worth looking into.

djowel avatar Jan 22 '20 14:01 djowel

I'm in the process of looking how other GUI libraries handle this and designing a common OS-agnostic API.

Windows and macOS interfaces are rather rich - what should I be looking at for unix systems? GTK+ 3 API?

Xeverous avatar May 03 '20 14:05 Xeverous

I started making a sheet which compares major OS APIs and libraries (Windows, Mac, GTK+ 3, wxWidgets 3, Qt 5). Turns out all of them have very rich set of settings.

Core functionality like path filtering, default file/dir are present in all. For each of other/advanced options, it is present on most targets.

Both Qt and wxWidgets cover almost all and ignore specific ones on platforms on which they are not available. IMO this is a very good design.

The eventual interface in elements can be a free function that takes 1 parameter: options struct. This way:

  • for each option elements can provide a very reasonable default value
  • we can add more options later without any API breakages
  • Elements library users can do what they want with the options struct. file-save-as / file-open and directory-open share a lot of common settings and it would be good to store them in a common member struct. This way library users can easily implement consistent behavior.

Xeverous avatar May 03 '20 19:05 Xeverous

Windows and macOS interfaces are rather rich - what should I be looking at for unix systems? GTK+ 3 API?

Yes GTK3 on Linux

djowel avatar May 03 '20 20:05 djowel

The eventual interface in elements can be a free function that takes 1 parameter: options struct. This way:

  • for each option elements can provide a very reasonable default value
  • we can add more options later without any API breakages
  • Elements library users can do what they want with the options struct. file-save-as / file-open and directory-open share a lot of common settings and it would be good to store them in a common member struct. This way library users can easily implement consistent behavior.

Sounds good to me! I suggest starting basic. It's always easy to add APIs than to remove APIs, especially when the library gets established.

djowel avatar May 03 '20 20:05 djowel

This is going to take some time, but I can share my WIP spreadsheet if you want.

Xeverous avatar May 03 '20 22:05 Xeverous

This is going to take some time, but I can share my WIP spreadsheet if you want.

Google sheet will be good.

djowel avatar May 03 '20 22:05 djowel

Actually, it's easier for me to work on the spreatsheet offline. I already switch too many browser tabs too often when reading various documentation. I can upload it here when I finish.

Xeverous avatar May 04 '20 17:05 Xeverous

OS API notes.xlsx

These are all of my current notes on all 3 filesystem dialogs:

  • save file
  • open file
  • open directory

Xeverous avatar May 04 '20 22:05 Xeverous

Wow, that's a lot! Nice work collecting that. So basically, as always: Start with a minimal subset that is available on all platforms.

(BTW. I wonder how you are able to test on MacOS, do you have a Mac you can work on?)

djowel avatar May 04 '20 22:05 djowel

I'm not able to test it on mac. I don't even have Obj-C skills. All from the sheet is from API documentation. I will left Mac implementation for you.

Xeverous avatar May 04 '20 23:05 Xeverous

I'm not able to test it on mac. I don't even have Obj-C skills. All from the sheet is from API documentation. I will left Mac implementation for you.

OK, make it work on Windows and Linux then, and I can take care of MacOS.

djowel avatar May 05 '20 01:05 djowel

Implementation question: most (if not all) platforms require to hold or pass some platform-specific state to create any modal window.

Should we make the modal functions members of the window class or should we make the window class to return std::function objects from these functions?

Xeverous avatar May 05 '20 11:05 Xeverous

I fkin love Microsoft...

They provide a sample usage code on https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/bb776913(v=vs.85)?redirectedfrom=MSDN but it is not a complete function.

Then this:

Note Several examples in this topic use the CDialogEventHandler_CreateInstance helper function to create an instance of the IFileDialogEvents implementation. To use this function in your own code, copy the source code for the CDialogEventHandler_CreateInstance function from the Common File Dialog Sample

The sample has its own page, except there is no code on it. Only 2 links:

  • download Windows 7 SDK installer, which contains all the samples
  • browse the sample code in Windows Code Gallery

The gallery link is basically 404 and the SDK installer can not finish it's job because it conflicts with already installed VC redistributable on my Windows (even if I select to install just the samples). MS docs say I need to uninstall existing redis first and then install them back after SDK is installed but I have no idea whether I will get correct versions back and whether something existing (which uses them) will broke. I just want the damn archive with sample code.

Fortunately there are good people that uploaded all of the samples to repos such as this one. It weights 300 MB and I have no idea what many of the directory names mean but fortunately a quick grep -rn "CDialogEventHandler_CreateInstance" . finds the thing I need.

Xeverous avatar May 05 '20 12:05 Xeverous

Funny thing: while reading the docs, I was concerned if they are up to date since 1 function was provided only 1 argument while it was declared to take 2. I pasted the code to the IDE but nothing was underlined.

I'm first time doing anything complex in Microsoft native API and it turns out there are many nasty things in their macros. IID_PPV_ARGS(&pfd) is actually __mingw_uuidof<__typeof(**(&pfd))>(), IID_PPV_ARGS_Helper (&pfd).

Xeverous avatar May 05 '20 15:05 Xeverous

Implementation question: most (if not all) platforms require to hold or pass some platform-specific state to create any modal window.

Should we make the modal functions members of the window class or should we make the window class to return std::function objects from these functions?

What does WX do? Keep in mind that when making plugins, you do not even have access to a window, which makes me wonder how this will all work out for that specific, and important use-case.

djowel avatar May 05 '20 15:05 djowel

Both WX and Qt implement these dialogs as a separate, standalone classes.

In both librariers, constructors require a pointer to the parent window (possibly null pointer, but that is hardly ever the case - modal dialogs are practically always invoked from already existing window).

In both libraries, there is a member function that will show the dialog and establish a new event loop for the time of the modal window.

In both libraries, some member functions have preconditions. Violating them results in undefined behavior.

Xeverous avatar May 05 '20 17:05 Xeverous

It should be standalone. Elements require components that do not require a parent window. It's one of the fundamental requirements. The Element 'window' and 'app' classes are actually optional and are never used when writing plugins.

djowel avatar May 05 '20 18:05 djowel

It should be standalone. Elements require components that do not require a parent window. It's one of the fundamental requirements. The Element 'window' and 'app' classes are actually optional and are never used when writing plugins.

Having said that, the base_view has a pointer to the 'child-window' (in windows), widget (in gtk3) and nsview (in macos), so if you know what you are doing, it is possible to spawn the modal via the internal base_view platform specific handle.

djowel avatar May 05 '20 18:05 djowel

Elements require components that do not require a parent window. It's one of the fundamental requirements.

The problem is, one can create multiple windows. And the modals on each system are window-blocking or application-blocking. I still haven't analyzed all of the dependencies on each system and what exactly is needed to create a modal.

I know that you want to have a completely disjoint types in the API, but it is not always possible. If there is any unavoidable coupling, IMO it is better to ilustrate it explictly in the API by making modals member functions of the window than to hide it with unaccessible, global variables.

Right now I'm writing OS-specific implementation and trying to satisfy the API, we will see where the problems arise.


Btw, 2 other things:

  1. I think it would be useful to expose these functions on the public API (on Windows only)

https://github.com/cycfi/elements/blob/773b60b6fd759a3687f9d654d92555d953ef9f7c/lib/host/windows/base_view.cpp#L43-L62

They are not needed on other systems but they help a lot when doing any stuff on Windows.

  1. Any progress on #23? I posted there a sample implementation.

Xeverous avatar May 05 '20 18:05 Xeverous

I have a working Windows partial implementation.

Interesting observation: there is no need to pass any state, but if I set the parent window to nullptr then the dialog is not modal.

So (at least on Windows) the window object can be completely decoupled from modal function (no state required to pass). This actually opens 2 separate behaviors on the API. This is good, because the library user can then choose to open a mode-less dialog or pass a reference to specific elements::window instance.

Let's continue and see what is observed further.

Xeverous avatar May 05 '20 20:05 Xeverous

You might want to look: 02f50e1

Xeverous avatar May 05 '20 20:05 Xeverous

You might want to look: 02f50e1

auto scale = 1.0;

???

Are you testing on visual studio on a HDPI machine? It will give me a lot of work if you are not. I respect your decision to use gcc, but keep in mind that this is not the mainstream compiler on Windows.

djowel avatar May 05 '20 23:05 djowel

  1. I think it would be useful to expose these functions on the public API (on Windows only)

Sure, but keep the header private. I usually do it in the same directory where the sources are and use include "some_util.hpp"

  1. Any progress on #23? I posted there a sample implementation.

No promises, but I'll see if I can give that top priority. Is it in the experimental branch already? If not please do.

djowel avatar May 05 '20 23:05 djowel

I respect your decision to use gcc, but keep in mind that this is not the mainstream compiler on Windows.

Of course these changes are not intended for the actual commit. I always revert any patches like this one but this time the area where the work is being done is conflicting with the patch so I just commited it and will then revert it when the full PR is ready.

Also, I don't have any HDPI device for such testing. All of the commited scaling changes are irrelevant for the issue, they are just to be able to compile with MinGW which does not (yet) have this API implemented. I could actually contact MinGW runtime devs and check/ask why this API is not yet complete. It's not that much new. Initially I thought the thing is already done as I used a bit older installation and didn't wanted to bother you with MinGW ifdef PRs but now I see that it might take more time than I expected. I will check the situation - I know that many of the MinGW stuff is autogenerated so it is either simple overlook on their side or there is a bigger issue behind.

No promises, but I'll see if I can give that top priority. Is it in the experimental branch already? If not please do.

Good to read this, you seemed recently to kind of ignore this issue and I'm fighting with for a long time already. Pushed it and now the HEAD of experimental has the sample tab implementation (with a very bad drawing implementation). Also note that during experimenting I discovered #98 which might be related some bigger problems (eg weird behavior of stretching).

Xeverous avatar May 06 '20 00:05 Xeverous

No promises, but I'll see if I can give that top priority. Is it in the experimental branch already? If not please do.

Good to read this, you seemed recently to kind of ignore this issue and I'm fighting with for a long time already. Pushed it and now the HEAD of experimental has the sample tab implementation (with a very bad drawing implementation). Also note that during experimenting I discovered #98 which might be related some bigger problems (eg weird behavior of stretching).

I am not ignoring the issue. There's just higher priority work already in the pipeline.

djowel avatar May 06 '20 00:05 djowel