/+ BreakpointSplitter - if not all widgets fit, it collapses to tabs - if they do, you get a splitter - you set priority to display things first and optional breakpoint (otherwise it uses flex basis and min width) +/ // http://msdn.microsoft.com/en-us/library/windows/desktop/bb775498%28v=vs.85%29.aspx // if doing nested menus, make sure the straight line from where it pops up to any destination on the new popup is not going to disappear the menu until at least a delay // me@arsd:~/.kde/share/config$ vim kdeglobals // FIXME: i kinda like how you can show find locations in scrollbars in the chrome browisers i wanna support that here too. // https://www.freedesktop.org/wiki/Accessibility/AT-SPI2/ // for responsive design, a collapsible widget that if it doesn't have enough room, it just automatically becomes a "more" button or whatever. // responsive minigui, menu search, and file open with a preview hook on the side. // FIXME: add menu checkbox and menu icon eventually // FIXME: checkbox menus and submenus and stuff // FOXME: look at Windows rebar control too /* im tempted to add some css kind of thing to minigui. i've not done in the past cuz i have a lot of virtual functins i use but i think i have an evil plan the virtual functions remain as the default calculated values. then the reads go through some proxy object that can override it... */ // FIXME: a popup with slightly shaped window pointing at the mouse might eb useful in places // FIXME: text label must be copyable to the clipboard, at least as a full chunk. // FIXME: opt-in file picker widget with image support // FIXME: number widget // https://www.codeguru.com/cpp/controls/buttonctrl/advancedbuttons/article.php/c5161/Native-Win32-ThemeAware-OwnerDraw-Controls-No-MFC.htm // https://docs.microsoft.com/en-us/windows/win32/controls/using-visual-styles // osx style menu search. // would be cool for a scroll bar to have marking capabilities // kinda like vim's marks just on clicks etc and visual representation // generically. may be cool to add an up arrow to the bottom too // // leave a shadow of where you last were for going back easily // So a window needs to have a selection, and that can be represented by a type. This is manipulated by various // functions like cut, copy, paste. Widgets can have a selection and that would assert teh selection ownership for // the window. // so what about context menus? // https://docs.microsoft.com/en-us/windows/desktop/Controls/about-custom-draw // FIXME: make the scroll thing go to bottom when the content changes. // add a knob slider view... you click and go up and down so basically same as a vertical slider, just presented as a round image // FIXME: the scroll area MUST be fixed to use the proper apis under the hood. // FIXME: add a command search thingy built in and implement tip. // FIXME: omg omg what if menu functions have arguments and it can pop up a gui or command line script them?! // On Windows: // FIXME: various labels look broken in high contrast mode // FIXME: changing themes while the program is upen doesn't trigger a redraw // add note about manifest to documentation. also icons. // a pager control is just a horizontal scroll area just with arrows on the sides instead of a scroll bar // FIXME: clear the corner of scrollbars if they pop up // minigui needs to have a stdout redirection for gui mode on windows writeln // I kinda wanna do state reacting. sort of. idk tho // need a viewer widget that works like a web page - arrows scroll down consistently // I want a nanovega widget, and a svg widget with some kind of event handlers attached to the inside. // FIXME: the menus should be a bit more discoverable, at least a single click to open the others instead of two. // and help info about menu items. // and search in menus? // FIXME: a scroll area event signaling when a thing comes into view might be good // FIXME: arrow key navigation and accelerators in dialog boxes will be a must // FIXME: unify Windows style line endings /* TODO: pie menu class Form with submit behavior -- see AutomaticDialog disabled widgets and menu items event cleanup tooltips. api improvements margins are kinda broken, they don't collapse like they should. at least. a table form btw would be a horizontal layout of vertical layouts holding each column that would give the same width things */ /* 1(15:19:48) NotSpooky: Menus, text entry, label, notebook, box, frame, file dialogs and layout (this one is very useful because I can draw lines between its child widgets */ /++ minigui is a smallish GUI widget library, aiming to be on par with at least HTML4 forms and a few other expected gui components. It uses native controls on Windows and does its own thing on Linux (Mac is not currently supported but I'm slowly working on it). $(H3 Conceptual Overviews) A gui application is made out of widgets laid out in windows that display information and respond to events from the user. They also typically have actions available in menus, and you might also want to customize the appearance. How do we do these things with minigui? Let's break it down into several categories. $(H4 Code structure) You will typically want to create the ui, prepare event handlers, then run an event loop. The event loop drives the program, calling your methods to respond to user activity. --- import arsd.minigui; void main() { // first, create a window, the (optional) string here is its title auto window = new MainWindow("Hello, World!"); // lay out some widgets inside the window to create the ui auto name = new LabeledLineEdit("What is your name?", window); auto button = new Button("Say Hello", window); // prepare event handlers button.addEventListener(EventType.triggered, () { window.messageBox("Hello, " ~ name.content ~ "!"); }); // show the window and run the event loop until this window is closed window.loop(); } --- To compile, run `opend hello.d`, then run the generated `hello` program. While the specifics will change, nearly all minigui applications will roughly follow this pattern. $(TIP There are two other ways to run event loops: `arsd.simpledisplay.EventLoop.get.run();` and `arsd.core.getThisThreadEventLoop().run();`. They all call the same underlying functions, but have different exit conditions - the `EventLoop.get.run()` keeps running until all top-level windows are closed, and `getThisThreadEventLoop().run` keeps running until all "tasks are resolved"; it is more abstract, supporting more than just windows. You may call this if you don't have a single main window. Even a basic minigui window can benefit from these if you don't have a single main window: --- import arsd.minigui; void main() { // create a struct to hold gathered info struct Hello { string name; } // let minigui create a dialog box to get that // info from the user. If you have a main window, // you'd pass that here, but it is not required dialog((Hello info) { // inline handler of the "OK" button messageBox("Hello, " ~ info.name); }); // since there is no main window to loop on, // we instead call the event loop singleton ourselves EventLoop.get.run; } --- This is also useful when your programs lives as a notification area (aka systray) icon instead of as a window. But let's not get too far ahead of ourselves! ) $(H4 How to lay out widgets) To better understand the details of layout algorithms and see more available included classes, see [Layout]. $(H5 Default layouts) minigui windows default to a flexible vertical layout, where widgets are added, from top to bottom on the window, in the same order of you creating them, then they are sized according to layout hints on the widget itself to fill the available space. This gives a reasonably usable setup but you'll probably want to customize it. $(TIP minigui's default [VerticalLayout] and [HorizontalLayout] are roughly based on css flexbox with wrap turned off. ) Generally speaking, there are two ways to customize layouts: either subclass the widget and change its hints, or wrap it in another layout widget. You can also create your own layout classes and do it all yourself, but that's fairly complicated. Wrapping existing widgets in other layout widgets is usually the easiest way to make things work. $(NOTE minigui widgets are not supposed to overlap, but can contain children, and are always rectangular. Children are laid out as rectangles inside the parent's rectangular area. ) For example, to display two widgets side-by-side, you can wrap them in a [HorizontalLayout]: --- import arsd.minigui; void main() { auto window = new MainWindow(); // make the layout a child of our window auto hl = new HorizontalLayout(window); // then make the widgets children of the layout auto leftButton = new Button("Left", hl); auto rightButton = new Button("Right", hl); window.loop(); } --- A [HorizontalLayout] works just like the default [VerticalLayout], except in the other direction. These two buttons will take up all the available vertical space, then split available horizontal space equally. $(H5 Nesting layouts) Nesting layouts lets you carve up the rectangle in different ways. $(EMBED_UNITTEST layout-example) $(H5 Special layouts) [TabWidget] can show pages of layouts as tabs. See [ScrollableWidget] but be warned that it is weird. You might want to consider something like [GenericListViewWidget] instead. $(H5 Other common layout classes) [HorizontalLayout], [VerticalLayout], [InlineBlockLayout], [GridLayout] $(H4 How to respond to widget events) To better understanding the underlying event system, see [Event]. Each widget emits its own events, which propagate up through their parents until they reach their top-level window. $(H4 How to do overall ui - title, icons, menus, toolbar, hotkeys, statuses, etc.) We started this series with a [MainWindow], but only added widgets to it. MainWindows also support menus and toolbars with various keyboard shortcuts. You can construct these menus by constructing classes and calling methods, but minigui also lets you just write functions in a command object and it does the rest! See [MainWindow.setMenuAndToolbarFromAnnotatedCode] for an example. Note that toggleable menu or toolbar items are not yet implemented, but on the todolist. Submenus and disabled items are also not supported at this time and not currently on the work list (but if you need it, let me know and MAYBE we can work something out. Emphasis on $(I maybe)). $(TIP The automatic dialog box logic is also available for you to invoke on demand with [dialog] and the data setting logic can be used with a child widget inside an existing window [addDataControllerWidget], which also has annotation-based layout capabilities. ) All windows also have titles. You can change this at any time with the `window.title = "string";` property. Windows also have icons, which can be set with the `window.icon` property. It takes a [arsd.color.MemoryImage] object, which is an in-memory bitmap. [arsd.image] can load common file formats into these objects, or you can make one yourself. The default icon on Windows is the icon of your exe, which you can set through a resource file. (FIXME: explain how to do this easily.) The `MainWindow` also provides a status bar across the bottom. These aren't so common in new applications, but I love them - on my own computer, I even have a global status bar for my whole desktop! I suggest you use it: a status bar is a consistent place to put information and notifications that will never overlap other content. A status bar has parts, and the parts have content. The first part's content is assumed to change frequently; the default mouse over event will set it to [Widget.statusTip], a public `string` you can assign to any widget you want at any time. Other parts can be added by you and are under your control. You add them with: --- window.statusBar.parts ~= StatusBar.Part(optional_size, optional_units); --- The size can be in a variety of units and what you get with mixes can get complicated. The rule is: explicit pixel sizes are used first. Then, proportional sizes are applied to the remaining space. Then, finally, if there is any space left, any items without an explicit size split them equally. You may prefer to set them all at once, with: --- window.statusBar.parts.setSizes(1, 1, 1); --- This makes a three-part status bar, each with the same size - they all take the same proportion of the total size. Negative numbers here will use auto-scaled pixels. You should call this right after creating your `MainWindow` as part of your setup code. Once you make parts, you can explicitly change their content with `window.statusBar.parts[index].content = "some string";` $(NOTE I'm thinking about making the other parts do other things by default too, but if I do change it, I'll try not to break any explicitly set things you do anyway. ) If you really don't want a status bar on your main window, you can remove it with `window.statusBar = null;` Make sure you don't try to use it again, or your program will likely crash! Status bars, at this time, cannot hold non-text content, but I do want to change that. They also cannot have event listeners at this time, but again, that is likely to change. I have something in mind where they can hold clickable messages with a history and maybe icons, but haven't implemented any of that yet. Right now, they're just a (still very useful!) display area. $(H4 How to do custom styles) Minigui's custom widgets support styling parameters on the level of individual widgets, or application-wide with [VisualTheme]s. $(WARNING These don't apply to non-custom widgets! They will use the operating system's native theme unless the documentation for that specific class says otherwise. At this time, custom widgets gain capability in styling, but lose capability in terms of keeping all the right integrated details of the user experience and availability to accessibility and other automation tools. Evaluate if the benefit is worth the costs before making your decision. I'd like to erase more and more of these gaps, but no promises as to when - or even if - that will ever actually happen. ) See [Widget.Style] for more information. $(H4 Selection of categorized widgets) $(LIST * Buttons: [Button] * Text display widgets: [TextLabel], [TextDisplay] * Text edit widgets: [LineEdit] (and [LabeledLineEdit]), [PasswordEdit] (and [LabeledPasswordEdit]), [TextEdit] * Selecting multiple on/off options: [Checkbox] * Selecting just one from a list of options: [Fieldset], [Radiobox], [DropDownSelection] * Getting rough numeric input: [HorizontalSlider], [VerticalSlider] * Displaying data: [ImageBox], [ProgressBar], [TableView] * Showing a list of editable items: [GenericListViewWidget] * Helpers for building your own widgets: [OpenGlWidget], [ScrollMessageWidget] ) And more. See [#members] until I write up more of this later and also be aware of the package [arsd.minigui_addons]. If none of these do what you need, you'll want to write your own. More on that in the following section. $(H4 custom widgets - how to write your own) See some example programs: https://github.com/adamdruppe/minigui-samples When you can't build your application out of existing widgets, you'll want to make your own. The general pattern is to subclass [Widget], write a constructor that takes a `Widget` parent argument you pass to `super`, then set some values, override methods you want to customize, and maybe add child widgets and events as appropriate. You might also be able to subclass an existing other Widget and customize that way. To get more specific, let's consider a few illustrative examples, then we'll come back to some principles. $(H5 Custom Widget Examples) $(H5 More notes) See [Widget]. If you override [Widget.recomputeChildLayout], don't forget to call `registerMovement()` at the top of it, then call recomputeChildLayout of all its children too! If you need a nested OS level window, see [NestedChildWindowWidget]. Use [Widget.scaleWithDpi] to convert logical pixels to physical pixels, as required. See [Widget.OverrideStyle], [Widget.paintContent], [Widget.dynamicState] for some useful starting points. You may also want to provide layout and style hints by overriding things like [Widget.flexBasisWidth], [Widget.flexBasisHeight], [Widget.minHeight], yada, yada, yada. You might make a compound widget out of other widgets. [Widget.encapsulatedChildren] can help hide this from the outside world (though is not necessary and might hurt some debugging!) $(TIP Compile your application with the `-debug` switch and press F12 in your window to open a web-browser-inspired debug window. It sucks right now and doesn't do a lot, but is sometimes better than nothing. ) $(H5 Timers and animations) The [Timer] class is available and you can call `widget.redraw();` to trigger a redraw from a timer handler. I generally don't like animations in my programs, so it hasn't been a priority for me to do more than this. I also hate uis that move outside of explicit user action, so minigui kinda supports this but I'd rather you didn't. I kinda wanna do something like `requestAnimationFrame` or something but haven't yet so it is just the `Timer` class. $(H5 Clipboard integrations, drag and drop) GUI application users tend to expect integration with their system, so clipboard support is basically a must, and drag and drop is nice to offer too. The functions for these are provided in [arsd.simpledisplay], which is public imported from minigui, and thus available to you here too. I'd like to think of some better abstractions to make this more automagic, but you must do it yourself when implementing your custom widgets right now. See: [draggable], [DropHandler], [setClipboardText], [setClipboardImage], [getClipboardText], [getClipboardImage], [setPrimarySelection], and others from simpledisplay. $(H5 Context menus) Override [Widget.contextMenu] in your subclass. $(H4 Coming later) Among the unfinished features: unified selections, translateable strings, external integrations. $(H2 Running minigui programs) Note the environment variable ARSD_SCALING_FACTOR on Linux can set multi-monitor scaling factors. I should also read it from a root window property so it easier to do with migrations... maybe a default theme selector from there too. $(H2 Building minigui programs) minigui's only required dependencies are [arsd.simpledisplay], [arsd.color], and [arsd.textlayouter], on which it is built. simpledisplay provides the low-level interfaces and minigui builds the concept of widgets inside the windows on top of it. Its #1 goal is to be useful without being large and complicated like GTK and Qt. It isn't hugely concerned with appearance - on Windows, it just uses the native controls and native theme, and on Linux, it keeps it simple and I may change that at any time, though after May 2021, you can customize some things with css-inspired [Widget.Style] classes. (On Windows, if you compile with `-version=custom_widgets`, you can use the custom implementation there too, but... you shouldn't.) The event model is similar to what you use in the browser with Javascript and the layout engine tries to automatically fit things in, similar to a css flexbox. FOR BEST RESULTS: be sure to link with the appropriate subsystem command `-L/SUBSYSTEM:WINDOWS` and -L/entry:mainCRTStartup`. If using ldc instead of dmd, use `-L/entry:wmainCRTStartup` instead of `mainCRTStartup`; note the "w". Otherwise you'll get a console and possibly other visual bugs. But if you do use the subsystem:windows, note that Phobos' writeln will crash the program! HTML_To_Classes: $(SMALL_TABLE HTML Code | Minigui Class `` | [LineEdit] `` | [PasswordEdit] `