diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e243c..0468eea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ add_library( include/wxUI/Choice.h include/wxUI/ComboBox.h include/wxUI/Custom.h + include/wxUI/GetterSetter.h include/wxUI/Hyperlink.h include/wxUI/Layout.h include/wxUI/Line.h diff --git a/README.md b/README.md index 2625869..3442396 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,15 @@ C++ header-only library to make declarative UIs for wxWidgets. ## Quick Start ```cpp +#include #include #include class ExampleDialog : public wxDialog { public: explicit ExampleDialog(wxWindow* parent); + wxUI::SpinCtrl::Proxy a, b; + wxUI::Text::Proxy result; }; ExampleDialog::ExampleDialog(wxWindow* parent) @@ -18,6 +21,7 @@ ExampleDialog::ExampleDialog(wxWindow* parent) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { using namespace wxUI; + VSizer { wxSizerFlags().Expand().Border(), VSizer { @@ -32,6 +36,20 @@ ExampleDialog::ExampleDialog(wxWindow* parent) .withStyle(wxTE_MULTILINE) .withSize(wxSize(200, 100)) }, + HSizer { + Text { "A =" }, + a = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { + *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); + }), + + Text { "B =" }, + b = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); }), + + Text { "GCD = " }, + result = Text { "1" }, + }, RadioBox { "&Log Levels:", { "&Information", "&Warning", "&Error", "&None", "&Custom" } } .withStyle(wxRA_SPECIFY_ROWS) .withMajorDim(1) @@ -55,7 +73,7 @@ ExampleDialog::ExampleDialog(wxWindow* parent) Generic { CreateStdDialogButtonSizer(wxOK) }, } - .attachTo(this); + .attachToAndFit(this); } ``` @@ -103,6 +121,7 @@ ExampleDialog::ExampleDialog(wxWindow* parent) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { using namespace wxUI; + VSizer { wxSizerFlags().Expand().Border(), VSizer { @@ -112,9 +131,23 @@ ExampleDialog::ExampleDialog(wxWindow* parent) .withStyle(wxALIGN_LEFT), // ... }, + HSizer { + Text { "A =" }, + a = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { + *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); + }), + + Text { "B =" }, + b = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); }), + + Text { "GCD = " }, + result = Text { "1" }, + }, // ... } - .attachTo(this); + .attachToAndFit(this); } ``` diff --git a/docs/ProgrammersGuide.md b/docs/ProgrammersGuide.md index ba09950..9d90631 100644 --- a/docs/ProgrammersGuide.md +++ b/docs/ProgrammersGuide.md @@ -5,7 +5,7 @@ C++ header-only library to make declarative UIs for wxWidgets. This overview provides an overview of `wxUI`, but is not intented to be a tutorial on `wxWidgets`. We assume the reader has working knowledge of `wxWidgets`. Great documentation and guides are available at [https://www.wxwidgets.org](https://www.wxwidgets.org). -In `wxUI`, you use "Menu" to declary the layout of your menu items and the "actions" they call. Similarly, you use "Sizers" to declare the layout of "Controllers" for your application. +In `wxUI`, you use `wxUI::Menu` to declare the layout of your menu items and the "actions" they call. Similarly, you use `wxUI::Sizer` to declare the layout of *Controllers* for your application. ### Menu @@ -35,17 +35,21 @@ HelloWorldFrame::HelloWorldFrame() In `wxWidgets` the general paradigm is to create an enumeration of identity ints that you associate with a member, then you would bind, either statically or dynamically, to a function. With `wxUI::Menu` the construction of the identify and assocation with a function is handled automatically. By default `wxUI::Menu` starts the enumeration with `wxID_AUTO_LOWEST` and increments for each item. Take caution if you use these enumerations as it may collide with other ids assocated with the frame. -The top level `MenuBar` holds a collection of `Menu` objects. The `Menu` object consists of a name of the menu, and a collection of "Items", which can be one of `Item` (normal), `Separator`, `CheckItem`, and `RadioItem`. +The top level `wxUI::MenuBar` holds a collection of `wxUI::Menu` objects. The `wxUI::Menu` object consists of a name of the menu, and a collection of "Items", which can be one of `wxUI::Item` (normal), `wxUI::Separator`, `wxUI::CheckItem`, and `wxUI::RadioItem`. Menu Items are generally a name with a handler lambda, or name and id with a lambda. Menu Items can also be assocated with `wxStandardID`. Many of these like `wxID_EXIT` and `wxID_HELP` have predefined name, help, and handlers, so declaration with just an ID is allowed. Handlers are callable items that handle events. The handler can be declared with both no arguments or the `wxCommandEvent` argument for deeper inspection of the event. ```cpp - wxUI::Item { "&Example1...\tCtrl-D", [] { + wxUI::Separator {}, wxUI::Item { "&ExtendedExample...", [this] { + ExtendedExample dialog(this); + dialog.ShowModal(); + } }, + wxUI::Item { "&Example Item...", [] { wxLogMessage("Hello World!"); } }, - wxUI::CheckItem { "&Example2...\tCtrl-D", [](wxCommandEvent& event) { + wxUI::CheckItem { "&Example Checked Item...", [](wxCommandEvent& event) { wxLogMessage(event.IsChecked() ? "is checked" : "is not checked"); } }, ``` @@ -63,7 +67,7 @@ Items { "Name", Handler } Items { "Name", "Help", Handler } ``` -wxUI::Menu also allows nesting of menus. This allows complicated menus to be composed easily. +`wxUI::Menu` also allows nesting of menus. This allows complicated menus to be composed easily. ```cpp wxUI::Menu { @@ -77,6 +81,16 @@ wxUI::Menu also allows nesting of menus. This allows complicated menus to be co } }, }, // ... + wxUI::Separator {}, wxUI::Item { "&ExtendedExample...", [this] { + ExtendedExample dialog(this); + dialog.ShowModal(); + } }, + wxUI::Item { "&Example Item...", [] { + wxLogMessage("Hello World!"); + } }, + wxUI::CheckItem { "&Example Checked Item...", [](wxCommandEvent& event) { + wxLogMessage(event.IsChecked() ? "is checked" : "is not checked"); + } }, }, ``` @@ -85,7 +99,7 @@ The `wxUI::MenuBar` and related objects are generally "lazy" objects. They hold ### Layout -The basics of `wxUI` layout is the "Layout". You use a specific type of "Layout", with the `VSizer` (Vertical Sizer or "row") and `HSizer` (Horizontal Sizer or "column") being the most common. When a "Layout" is set as the top level, it uses the layout as a sort of "blueprint" for stamping out the UI by constructing the ownership hierarchy and layout. +The basics of `wxUI` layout is the *Layout*. You use a specific type of *Layout*, with the `wxUI::VSizer` (Vertical Sizer or "row") and `wxUI::HSizer` (Horizontal Sizer or "column") being the most common. When a *Layout* is set as the top level, it uses the layout as a sort of "blueprint" for stamping out the UI by constructing the ownership hierarchy and layout. ```cpp VSizer { @@ -94,12 +108,12 @@ The basics of `wxUI` layout is the "Layout". You use a specific type of "Layout "Text examples", // ... } - .attachTo(this); + .attachToAndFit(this); ``` In the above example we have constructed a vertical layout sizer that will use a `wxSizer` with the `wxSizerFlags` set to expand with a default border. Then the first item in the sizer is a second layer sizer with horizontal layout. The `wxSizerFlags` are propogated to each layer so the horizontal layout in this example would also be set to expand with a default border. The second sizer would be created as a "named" box horizonal sizer. -A "Layout" takes a collection of Items, which can be either additional "Layout" (to create a tree of "Layouts") or "Controllers". Here is the general form of constructions for Sizers: +A *Layout* takes a collection of objects, which can be either additional *Layout* (to create a tree of *Layouts*) or *Controllers*. Here is the general form of constructions for *Sizers*: ``` Sizer { Items... } @@ -108,9 +122,9 @@ Sizer { "Name", Items... } Sizer { "Name", SizerFlags, Items... } ``` -`wxUI` supports 3 flavors of Sizers: `VSizer` (Vertical Sizers), `HSizer` (Horizontal Sizers), and `FlexGridSizer` (Flexible Grid Sizers). Both `VSizer` and `HSizer` can be created with a string to create a "named" box. +`wxUI` supports 3 flavors of Sizers: `wxUI::VSizer` (Vertical Sizers), `wxUI::HSizer` (Horizontal Sizers), and `wxUI::FlexGridSizer` (Flexible Grid Sizers). Both `wxUI::VSizer` and `wxUI::HSizer` can be created with a string to create a "named" box. -Note: Because Sizers are intented to be "recursive" data structures, it is possible for a `VSizer` to contain a `VSizer`. However, be aware that if an empty `VSizer` is created with *just* a `VSizer` as the argument, we collapse that to be a single `VSizer`. ie, this: +Note: Because Sizers are intented to be "recursive" data structures, it is possible for a `wxUI::VSizer` to contain a `wxUI::VSizer`. However, be aware that if an empty `wxUI::VSizer` is created with *just* a `wxUI::VSizer` as the argument, we collapse that to be a single `wxUI::VSizer`. ie, this: ``` wxUI::VSizer { wxUI::VSizer { "Current Frame" } }.attachTo(this); @@ -125,7 +139,7 @@ wxUI::VSizer { "Current Frame" }.attachTo(this); #### Generic -One special type of "Layout" is `Generic`. There are cases where you may have an existing layout as a `wxSizer` (such as a common dialog) or `wxWindow` (such as a custom window) that you wish to use with `wxUI`. This is a case to use `Generic`: +One special type of *Layout* is `Generic`. There are cases where you may have an existing layout as a `wxSizer` (such as a common dialog) or `wxWindow` (such as a custom window) that you wish to use with `wxUI`. This is a case to use `Generic`: ```cpp VSizer { @@ -133,12 +147,12 @@ One special type of "Layout" is `Generic`. There are cases where you may have a // ... Generic { CreateStdDialogButtonSizer(wxOK) }, } - .attachTo(this); + .attachToAndFit(this); ``` ### Controllers -"Controllers" are the general term to refer to items that behave like a [`wxContol`](https://docs.wxwidgets.org/3.0/classwx_control.html). In `wxUI` we attempt to conform a consistent style that favors the common things you do with a specific `wxControl`. +*Controllers* are the general term to refer to items that behave like a [`wxContol`](https://docs.wxwidgets.org/3.0/classwx_control.html). In `wxUI` we attempt to conform a consistent style that favors the common things you do with a specific `wxControl`. ```cpp HSizer { @@ -150,7 +164,7 @@ One special type of "Layout" is `Generic`. There are cases where you may have a }, ``` -By default "Controllers" are constructed using the default arguments for position, style, size, etc. "Controllers" are designed to use Method Chaining to specialize the way the controller is constructed. In the example above we see that `TextCtrl` is being augmented with the style `wxALIGN_LEFT`. +By default *Controllers* are constructed using the default arguments for position, style, size, etc. *Controllers* are designed to use [Method Chaining](https://en.wikipedia.org/wiki/Method_chaining) to specialize the way the controller is constructed. In the example above we see that `wxUI::TextCtrl` is being augmented with the style `wxALIGN_LEFT`. The list of Methods supported by all controllers: @@ -158,30 +172,9 @@ The list of Methods supported by all controllers: * `withSize(wxSize size)` : Specifies the `size` of the `wxControl`. * `withStyle(long style)` : Specifies the `style` of the `wxControl`. -The "Controllers" currently supported by `wxUI`: - - * `Bitmap` for `wxStaticBitmap` - * `BitmapButton` for `wxBitmapButton` - * `BitmapComboBox` for `wxBitmapComboBox` - * `BitmapToggleButton` for `wxBitmapToggleButton` - * `Button` for `wxButton` - * `CheckBox` for `wxCheckBox` - * `Choice` for `wxChoice` - * `ComboBox` for `wxComboBox` - * `Hypertext` for `wxHypertextCtrl` - * `ListBox` for `wxListBox` - * `Line` for `wxStaticLine` - * `RadioBox` for `wxRadioBox` - * `Slider` for `wxSlider` - * `SpinCtrl` for `wxSpinCtrl` - * `Text` for `wxStaticText` - * `TextCtrl` for `wxTextCtrl` - -Additional "Contollers" should be easy to add in future updates. - #### Bind -Some "Controllers" support "Binding" a function call to their event handlers. When the event for that controller is emitted, the function-like object supplied will be called. +Some *Controllers* support "binding" a function call to their event handlers. When the event for that controller is emitted, the function-like object supplied will be called. ```cpp Button { wxSizerFlags().Border(wxRIGHT), "Left" } @@ -192,9 +185,16 @@ Some "Controllers" support "Binding" a function call to their event handlers. W For convenience the event parameter of the function can be omitted in cases where it is unused. -#### `getHandle` +#### Proxy + +Often the value of a *Controller* in a layout needs to be referenced, or sometimes the backing `wxWindow` itself needs to be used directly. This could be for reading a currently typed in value in a `TextCtrl`, or to change the selection of a `Choice`. *Controllers* support `Proxy` objects, a way to get the handle to the underlying `wxWindow` that is created for the *Controller*. + +Some *Controllers* do not support values that are intended to change, such as a `Line`, and others can have several values of interest, such as a `ComboBox`. `Proxy` objects can have several accessors that allow access to these, most commonly called `value()` and `selection()` (see Supported Controllers for details of each supported *Controller*). These accessors are proxy objects support `get()` and `set()` functions, as well as a set of appropriate overloads for the underlying type, allowing more ergonomic interaction with the code. `Proxy` also supplies `operator*` and `operator->` which reference the most common accessor. + +`Proxy` supply `control()`, which is intended to allow access to the underlying controller. + +As `Proxy` objects need to be a named variable that exist outside of a *Controller*, and require being "attached". This is done with the `operator=`, allowing for an ergonomic way to attach `Proxy` objects to controls. Accessing a proxy object that has not been attached to a controller will cause an exception to be raised. -Often a `wxWindow` object needs to be referenced. For example, perhaps for reading a currently selected value. "Controllers" support `getHandle`, a way to get the handle to the underlying `wxWindow` that is created for the "Controller". You provide the address of the handle that you would like to be populated when the "Controller" is created ```cpp auto getHandle(UnderlyingType** handlePtr); @@ -213,15 +213,36 @@ ExtendedExample::ExtendedExample(wxWindow* parent) { using namespace wxUI; VSizer { - TextCtrl { "Hello" } - .getHandle(&mText), - }, + proxy = TextCtrl { "Hello" } } .attachTo(this); } ``` -Note that this handle is non-owning, so it is vital to not have the ptr participate in any lifecycle management of the object. +#### Supported Controllers + +The "Controllers" currently supported by `wxUI`: + +| wxUI | wxWidget | Proxy | Proxy accessors value | +| :------------------- | :--------------------- | :------------------------ | :-------------- | +| `Bitmap` | `wxStaticBitmap` | `BitmapProxy` | n/a | +| `BitmapButton` | `wxBitmapButton` | `BitmapButtonProxy` | n/a | +| `BitmapComboBox` | `wxBitmapComboBox` | `BitmapComboBoxProxy` | `selection` -> `int`
`value` -> `std::string`
*default*: `value` | +| `BitmapToggleButton` | `wxBitmapToggleButton` | `BitmapToggleButtonProxy` | `value` -> `bool`
*default*: `value` | +| `Button` | `wxButton` | `ButtonProxy` | n/a | +| `CheckBox` | `wxCheckBox` | `CheckBoxProxy` | `value` -> `bool`
*default*: `value` | +| `Choice` | `wxChoice` | `ChoiceProxy` | `selection` -> `int`
*default*: `selection` | +| `ComboBox` | `wxComboBox` | `ComboBoxProxy` | `selection` -> `int`
`value` -> `std::string`
*default*: `value` | +| `Hypertext` | `wxHypertextCtrl` | `HypertextProxy` | n/a | +| `Line` | `wxStaticLine` | `LineProxy` | n/a | +| `ListBox` | `wxListBox` | `ListBoxProxy` | `selection` -> `int`
*default*: `selection` | +| `RadioBox` | `wxRadioBox` | `RadioBoxProxy` | `selection` -> `int`
*default*: `selection` | +| `Slider` | `wxSlider` | `SliderProxy` | `value` -> `int`
*default*: `value` | +| `SpinCtrl` | `wxSpinCtrl` | `SpinCtrlProxy` | `value` -> `int`
*default*: `value` | +| `Text` | `wxStaticText` | `TextProxy` | `label` -> `std::string`
*default*: `label` | +| `TextCtrl` | `wxTextCtrl` | `TextCtrlProxy` | `label` -> `std::string`
*default*: `label` | + +Additional "Contollers" should be easy to add in future updates. #### Custom @@ -247,6 +268,7 @@ You would then create the controller to confomr }, }, }, + Generic { CreateStdDialogButtonSizer(wxOK) }, ``` #### Misc notes. diff --git a/docs/src/docs/ProgrammersGuide.md b/docs/src/docs/ProgrammersGuide.md index c596cd2..196e51d 100644 --- a/docs/src/docs/ProgrammersGuide.md +++ b/docs/src/docs/ProgrammersGuide.md @@ -5,7 +5,7 @@ C++ header-only library to make declarative UIs for wxWidgets. This overview provides an overview of `wxUI`, but is not intented to be a tutorial on `wxWidgets`. We assume the reader has working knowledge of `wxWidgets`. Great documentation and guides are available at [https://www.wxwidgets.org](https://www.wxwidgets.org). -In `wxUI`, you use "Menu" to declary the layout of your menu items and the "actions" they call. Similarly, you use "Sizers" to declare the layout of "Controllers" for your application. +In `wxUI`, you use `wxUI::Menu` to declare the layout of your menu items and the "actions" they call. Similarly, you use `wxUI::Sizer` to declare the layout of *Controllers* for your application. ### Menu @@ -19,7 +19,7 @@ The general concept is you declare a set of structures and then `attachTo` a fra In `wxWidgets` the general paradigm is to create an enumeration of identity ints that you associate with a member, then you would bind, either statically or dynamically, to a function. With `wxUI::Menu` the construction of the identify and assocation with a function is handled automatically. By default `wxUI::Menu` starts the enumeration with `wxID_AUTO_LOWEST` and increments for each item. Take caution if you use these enumerations as it may collide with other ids assocated with the frame. -The top level `MenuBar` holds a collection of `Menu` objects. The `Menu` object consists of a name of the menu, and a collection of "Items", which can be one of `Item` (normal), `Separator`, `CheckItem`, and `RadioItem`. +The top level `wxUI::MenuBar` holds a collection of `wxUI::Menu` objects. The `wxUI::Menu` object consists of a name of the menu, and a collection of "Items", which can be one of `wxUI::Item` (normal), `wxUI::Separator`, `wxUI::CheckItem`, and `wxUI::RadioItem`. Menu Items are generally a name with a handler lambda, or name and id with a lambda. Menu Items can also be assocated with `wxStandardID`. Many of these like `wxID_EXIT` and `wxID_HELP` have predefined name, help, and handlers, so declaration with just an ID is allowed. @@ -42,7 +42,7 @@ Items { "Name", Handler } Items { "Name", "Help", Handler } ``` -wxUI::Menu also allows nesting of menus. This allows complicated menus to be composed easily. +`wxUI::Menu` also allows nesting of menus. This allows complicated menus to be composed easily. ```cpp {{{ examples/HelloWorld/HelloWorld.cpp wxUIMenuSubMenu " // ..." }}} @@ -53,7 +53,7 @@ The `wxUI::MenuBar` and related objects are generally "lazy" objects. They hold ### Layout -The basics of `wxUI` layout is the "Layout". You use a specific type of "Layout", with the `VSizer` (Vertical Sizer or "row") and `HSizer` (Horizontal Sizer or "column") being the most common. When a "Layout" is set as the top level, it uses the layout as a sort of "blueprint" for stamping out the UI by constructing the ownership hierarchy and layout. +The basics of `wxUI` layout is the *Layout*. You use a specific type of *Layout*, with the `wxUI::VSizer` (Vertical Sizer or "row") and `wxUI::HSizer` (Horizontal Sizer or "column") being the most common. When a *Layout* is set as the top level, it uses the layout as a sort of "blueprint" for stamping out the UI by constructing the ownership hierarchy and layout. ```cpp {{{ examples/HelloWorld/HelloWorld.cpp wxUILayoutBasic " // ..." }}} @@ -61,7 +61,7 @@ The basics of `wxUI` layout is the "Layout". You use a specific type of "Layout In the above example we have constructed a vertical layout sizer that will use a `wxSizer` with the `wxSizerFlags` set to expand with a default border. Then the first item in the sizer is a second layer sizer with horizontal layout. The `wxSizerFlags` are propogated to each layer so the horizontal layout in this example would also be set to expand with a default border. The second sizer would be created as a "named" box horizonal sizer. -A "Layout" takes a collection of Items, which can be either additional "Layout" (to create a tree of "Layouts") or "Controllers". Here is the general form of constructions for Sizers: +A *Layout* takes a collection of objects, which can be either additional *Layout* (to create a tree of *Layouts*) or *Controllers*. Here is the general form of constructions for *Sizers*: ``` Sizer { Items... } @@ -70,9 +70,9 @@ Sizer { "Name", Items... } Sizer { "Name", SizerFlags, Items... } ``` -`wxUI` supports 3 flavors of Sizers: `VSizer` (Vertical Sizers), `HSizer` (Horizontal Sizers), and `FlexGridSizer` (Flexible Grid Sizers). Both `VSizer` and `HSizer` can be created with a string to create a "named" box. +`wxUI` supports 3 flavors of Sizers: `wxUI::VSizer` (Vertical Sizers), `wxUI::HSizer` (Horizontal Sizers), and `wxUI::FlexGridSizer` (Flexible Grid Sizers). Both `wxUI::VSizer` and `wxUI::HSizer` can be created with a string to create a "named" box. -Note: Because Sizers are intented to be "recursive" data structures, it is possible for a `VSizer` to contain a `VSizer`. However, be aware that if an empty `VSizer` is created with *just* a `VSizer` as the argument, we collapse that to be a single `VSizer`. ie, this: +Note: Because Sizers are intented to be "recursive" data structures, it is possible for a `wxUI::VSizer` to contain a `wxUI::VSizer`. However, be aware that if an empty `wxUI::VSizer` is created with *just* a `wxUI::VSizer` as the argument, we collapse that to be a single `wxUI::VSizer`. ie, this: ``` wxUI::VSizer { wxUI::VSizer { "Current Frame" } }.attachTo(this); @@ -87,7 +87,7 @@ wxUI::VSizer { "Current Frame" }.attachTo(this); #### Generic -One special type of "Layout" is `Generic`. There are cases where you may have an existing layout as a `wxSizer` (such as a common dialog) or `wxWindow` (such as a custom window) that you wish to use with `wxUI`. This is a case to use `Generic`: +One special type of *Layout* is `Generic`. There are cases where you may have an existing layout as a `wxSizer` (such as a common dialog) or `wxWindow` (such as a custom window) that you wish to use with `wxUI`. This is a case to use `Generic`: ```cpp {{{ examples/HelloWorld/HelloWorld.cpp wxUIGeneric " // ..." }}} @@ -95,13 +95,13 @@ One special type of "Layout" is `Generic`. There are cases where you may have a ### Controllers -"Controllers" are the general term to refer to items that behave like a [`wxContol`](https://docs.wxwidgets.org/3.0/classwx_control.html). In `wxUI` we attempt to conform a consistent style that favors the common things you do with a specific `wxControl`. +*Controllers* are the general term to refer to items that behave like a [`wxContol`](https://docs.wxwidgets.org/3.0/classwx_control.html). In `wxUI` we attempt to conform a consistent style that favors the common things you do with a specific `wxControl`. ```cpp {{{ examples/HelloWorld/HelloWorld.cpp wxUIController " // ..." }}} ``` -By default "Controllers" are constructed using the default arguments for position, style, size, etc. "Controllers" are designed to use Method Chaining to specialize the way the controller is constructed. In the example above we see that `TextCtrl` is being augmented with the style `wxALIGN_LEFT`. +By default *Controllers* are constructed using the default arguments for position, style, size, etc. *Controllers* are designed to use [Method Chaining](https://en.wikipedia.org/wiki/Method_chaining) to specialize the way the controller is constructed. In the example above we see that `wxUI::TextCtrl` is being augmented with the style `wxALIGN_LEFT`. The list of Methods supported by all controllers: @@ -109,30 +109,9 @@ The list of Methods supported by all controllers: * `withSize(wxSize size)` : Specifies the `size` of the `wxControl`. * `withStyle(long style)` : Specifies the `style` of the `wxControl`. -The "Controllers" currently supported by `wxUI`: - - * `Bitmap` for `wxStaticBitmap` - * `BitmapButton` for `wxBitmapButton` - * `BitmapComboBox` for `wxBitmapComboBox` - * `BitmapToggleButton` for `wxBitmapToggleButton` - * `Button` for `wxButton` - * `CheckBox` for `wxCheckBox` - * `Choice` for `wxChoice` - * `ComboBox` for `wxComboBox` - * `Hypertext` for `wxHypertextCtrl` - * `ListBox` for `wxListBox` - * `Line` for `wxStaticLine` - * `RadioBox` for `wxRadioBox` - * `Slider` for `wxSlider` - * `SpinCtrl` for `wxSpinCtrl` - * `Text` for `wxStaticText` - * `TextCtrl` for `wxTextCtrl` - -Additional "Contollers" should be easy to add in future updates. - #### Bind -Some "Controllers" support "Binding" a function call to their event handlers. When the event for that controller is emitted, the function-like object supplied will be called. +Some *Controllers* support "binding" a function call to their event handlers. When the event for that controller is emitted, the function-like object supplied will be called. ```cpp {{{ examples/HelloWorld/HelloWorld.cpp wxUIBind " // ..." }}} @@ -140,9 +119,16 @@ Some "Controllers" support "Binding" a function call to their event handlers. W For convenience the event parameter of the function can be omitted in cases where it is unused. -#### `getHandle` +#### Proxy + +Often the value of a *Controller* in a layout needs to be referenced, or sometimes the backing `wxWindow` itself needs to be used directly. This could be for reading a currently typed in value in a `TextCtrl`, or to change the selection of a `Choice`. *Controllers* support `Proxy` objects, a way to get the handle to the underlying `wxWindow` that is created for the *Controller*. + +Some *Controllers* do not support values that are intended to change, such as a `Line`, and others can have several values of interest, such as a `ComboBox`. `Proxy` objects can have several accessors that allow access to these, most commonly called `value()` and `selection()` (see Supported Controllers for details of each supported *Controller*). These accessors are proxy objects support `get()` and `set()` functions, as well as a set of appropriate overloads for the underlying type, allowing more ergonomic interaction with the code. `Proxy` also supplies `operator*` and `operator->` which reference the most common accessor. + +`Proxy` supply `control()`, which is intended to allow access to the underlying controller. + +As `Proxy` objects need to be a named variable that exist outside of a *Controller*, and require being "attached". This is done with the `operator=`, allowing for an ergonomic way to attach `Proxy` objects to controls. Accessing a proxy object that has not been attached to a controller will cause an exception to be raised. -Often a `wxWindow` object needs to be referenced. For example, perhaps for reading a currently selected value. "Controllers" support `getHandle`, a way to get the handle to the underlying `wxWindow` that is created for the "Controller". You provide the address of the handle that you would like to be populated when the "Controller" is created ```cpp auto getHandle(UnderlyingType** handlePtr); @@ -161,15 +147,36 @@ ExtendedExample::ExtendedExample(wxWindow* parent) { using namespace wxUI; VSizer { - TextCtrl { "Hello" } - .getHandle(&mText), - }, + proxy = TextCtrl { "Hello" } } .attachTo(this); } ``` -Note that this handle is non-owning, so it is vital to not have the ptr participate in any lifecycle management of the object. +#### Supported Controllers + +The "Controllers" currently supported by `wxUI`: + +| wxUI | wxWidget | Proxy | Proxy accessors value | +| :------------------- | :--------------------- | :------------------------ | :-------------- | +| `Bitmap` | `wxStaticBitmap` | `BitmapProxy` | n/a | +| `BitmapButton` | `wxBitmapButton` | `BitmapButtonProxy` | n/a | +| `BitmapComboBox` | `wxBitmapComboBox` | `BitmapComboBoxProxy` | `selection` -> `int`
`value` -> `std::string`
*default*: `value` | +| `BitmapToggleButton` | `wxBitmapToggleButton` | `BitmapToggleButtonProxy` | `value` -> `bool`
*default*: `value` | +| `Button` | `wxButton` | `ButtonProxy` | n/a | +| `CheckBox` | `wxCheckBox` | `CheckBoxProxy` | `value` -> `bool`
*default*: `value` | +| `Choice` | `wxChoice` | `ChoiceProxy` | `selection` -> `int`
*default*: `selection` | +| `ComboBox` | `wxComboBox` | `ComboBoxProxy` | `selection` -> `int`
`value` -> `std::string`
*default*: `value` | +| `Hypertext` | `wxHypertextCtrl` | `HypertextProxy` | n/a | +| `Line` | `wxStaticLine` | `LineProxy` | n/a | +| `ListBox` | `wxListBox` | `ListBoxProxy` | `selection` -> `int`
*default*: `selection` | +| `RadioBox` | `wxRadioBox` | `RadioBoxProxy` | `selection` -> `int`
*default*: `selection` | +| `Slider` | `wxSlider` | `SliderProxy` | `value` -> `int`
*default*: `value` | +| `SpinCtrl` | `wxSpinCtrl` | `SpinCtrlProxy` | `value` -> `int`
*default*: `value` | +| `Text` | `wxStaticText` | `TextProxy` | `label` -> `std::string`
*default*: `label` | +| `TextCtrl` | `wxTextCtrl` | `TextCtrlProxy` | `label` -> `std::string`
*default*: `label` | + +Additional "Contollers" should be easy to add in future updates. #### Custom diff --git a/examples/HelloWorld/ExtendedExample.cpp b/examples/HelloWorld/ExtendedExample.cpp index 3255e7b..4c0569a 100644 --- a/examples/HelloWorld/ExtendedExample.cpp +++ b/examples/HelloWorld/ExtendedExample.cpp @@ -26,15 +26,26 @@ SOFTWARE. #include "ExtendedExample.h" #include +wxUI::Text::Proxy textProxy; +wxUI::SpinCtrl::Proxy spinProxy; ExtendedExample::ExtendedExample(wxWindow* parent) : wxDialog(parent, wxID_ANY, "ExtendedExample", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { using namespace wxUI; - wxStaticText* text = nullptr; VSizer { wxSizerFlags().Expand().Border(), + // incr example + HSizer { + spinProxy.bind(SpinCtrl { std::pair { 1, 3 } }), + }, + HSizer { + Button { "Incr" } + .bind([]() { + *spinProxy = 1 + *spinProxy; + }), + }, HSizer { BitmapButton { wxBitmap {} }, }, @@ -44,6 +55,20 @@ ExtendedExample::ExtendedExample(wxWindow* parent) HSizer { BitmapToggleButton { wxBitmap {} }, }, + // getHandle example + HSizer { + textProxy = Text { "Hello" }, + }, + HSizer { + Button { "ReduceText" } + .bind([]() { + auto str = textProxy->get(); + if (str.size()) { + str.pop_back(); + } + textProxy->set(str); + }), + }, HSizer { Button {}, }, @@ -74,11 +99,6 @@ ExtendedExample::ExtendedExample(wxWindow* parent) HSizer { TextCtrl {}, }, - // getHandle example - HSizer { - Text { "Hello" } - .getHandle(&text), - }, // bind examples HSizer { TextCtrl { "Hello" } @@ -98,10 +118,8 @@ ExtendedExample::ExtendedExample(wxWindow* parent) }, }, }, + Generic { CreateStdDialogButtonSizer(wxOK) }, // endsnippet CustomExample } - .attachTo(this); - if (text->GetLabel() != "Hello") { - throw std::runtime_error("Error, could not create reference"); - } + .attachToAndFit(this); } diff --git a/examples/HelloWorld/HelloWorld.cpp b/examples/HelloWorld/HelloWorld.cpp index 0f46ce6..5c493b4 100644 --- a/examples/HelloWorld/HelloWorld.cpp +++ b/examples/HelloWorld/HelloWorld.cpp @@ -26,6 +26,7 @@ SOFTWARE. #include "ExtendedExample.h" // snippet Example headers to include +#include #include #include // endsnippet Example @@ -40,6 +41,12 @@ class HelloWorldFrame : public wxFrame { HelloWorldFrame(); }; +enum { + ExampleDialogWidgetsASpinner = 1100, + ExampleDialogWidgetsBSpinner, + ExampleDialogWidgetsGCDResult, +}; + class ExampleDialogWidgets : public wxDialog { public: explicit ExampleDialogWidgets(wxWindow* parent); @@ -49,6 +56,8 @@ class ExampleDialogWidgets : public wxDialog { class ExampleDialog : public wxDialog { public: explicit ExampleDialog(wxWindow* parent); + wxUI::SpinCtrl::Proxy a, b; + wxUI::Text::Proxy result; }; // endsnippet Example @@ -72,22 +81,10 @@ HelloWorldFrame::HelloWorldFrame() wxUI::Item { "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item", [] { wxLogMessage("Hello world from wxWidgets!"); } }, - // snippet wxUIMenuExample1 - wxUI::Item { "&Example1...\tCtrl-D", [] { - wxLogMessage("Hello World!"); - } }, - wxUI::CheckItem { "&Example2...\tCtrl-D", [](wxCommandEvent& event) { - wxLogMessage(event.IsChecked() ? "is checked" : "is not checked"); - } }, - // endsnippet wxUIMenuExample1 wxUI::Separator {}, wxUI::Item { "&Example...\tCtrl-D", [this] { ExampleDialogWidgets dialog(this); dialog.ShowModal(); } }, - wxUI::Separator {}, wxUI::Item { "&ExtendedExample...\tCtrl-D", [this] { - ExtendedExample dialog(this); - dialog.ShowModal(); - } }, // snippet wxUIMenu wxUI::Item { "&Example with wxUI...\tCtrl-F", [this] { ExampleDialog dialog(this); @@ -123,6 +120,18 @@ HelloWorldFrame::HelloWorldFrame() } }, }, // snippet wxUIMenuSubMenu + // snippet wxUIMenuExample1 + wxUI::Separator {}, wxUI::Item { "&ExtendedExample...", [this] { + ExtendedExample dialog(this); + dialog.ShowModal(); + } }, + wxUI::Item { "&Example Item...", [] { + wxLogMessage("Hello World!"); + } }, + wxUI::CheckItem { "&Example Checked Item...", [](wxCommandEvent& event) { + wxLogMessage(event.IsChecked() ? "is checked" : "is not checked"); + } }, + // endsnippet wxUIMenuExample1 }, // endsnippet wxUIMenuSubMenu wxUI::Menu { @@ -156,8 +165,15 @@ ExampleDialogWidgets::ExampleDialogWidgets(wxWindow* parent) "what the UI looks like.", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); - auto btnLeft = new wxButton(this, wxID_ANY, "Left"); - auto btnRight = new wxButton(this, wxID_ANY, "Right"); + auto* btnLeft = new wxButton(this, wxID_ANY, "Left"); + auto* btnRight = new wxButton(this, wxID_ANY, "Right"); + + auto* spinALabel = new wxStaticText(this, wxID_ANY, "A = "); + auto* spinA = new wxSpinCtrl(this, ExampleDialogWidgetsASpinner, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 1000, 1); + auto* spinBLabel = new wxStaticText(this, wxID_ANY, "B = "); + auto* spinB = new wxSpinCtrl(this, ExampleDialogWidgetsBSpinner, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 1000, 1); + auto* gcdLabel = new wxStaticText(this, wxID_ANY, "GCD = "); + auto* gcdResult = new wxStaticText(this, ExampleDialogWidgetsGCDResult, "1"); const wxString levels[] = { "&Information", @@ -194,6 +210,15 @@ ExampleDialogWidgets::ExampleDialogWidgets(wxWindow* parent) sizerText->Add(textBody, wxSizerFlags(1).Expand().Border()); sizer->Add(sizerText, wxSizerFlags(1).Expand().Border()); + auto* spinners = new wxBoxSizer(wxHORIZONTAL); + spinners->Add(spinALabel, wxSizerFlags().Expand().Border()); + spinners->Add(spinA, wxSizerFlags().Expand().Border()); + spinners->Add(spinBLabel, wxSizerFlags().Expand().Border()); + spinners->Add(spinB, wxSizerFlags().Expand().Border()); + spinners->Add(gcdLabel, wxSizerFlags().Expand().Border()); + spinners->Add(gcdResult, wxSizerFlags().Expand().Border()); + sizer->Add(spinners, wxSizerFlags().Expand().Border()); + sizer->Add(logLevels, wxSizerFlags().Expand().Border()); auto* sizerDetails = new wxStaticBoxSizer(wxHORIZONTAL, this, "Details"); @@ -207,7 +232,7 @@ ExampleDialogWidgets::ExampleDialogWidgets(wxWindow* parent) sizerBtns->Add(btnRight, wxSizerFlags().Border(wxRIGHT)); sizer->Add(sizerBtns, wxSizerFlags().Centre().Border()); - sizer->Add(new wxStaticLine(this, wxID_STATIC), 0, wxGROW | wxALL, 50); + sizer->Add(new wxStaticLine(this, wxID_STATIC), 0, wxGROW | wxALL); sizer->Add(CreateStdDialogButtonSizer(wxOK), wxSizerFlags().Expand().Border()); @@ -218,6 +243,15 @@ ExampleDialogWidgets::ExampleDialogWidgets(wxWindow* parent) // And connect the event handlers. btnLeft->Bind(wxEVT_BUTTON, [](wxEvent&) { wxLogMessage("Pressed Left"); }); btnRight->Bind(wxEVT_BUTTON, [](wxEvent&) { wxLogMessage("Pressed Right"); }); + + auto action = [this](wxEvent&) { + auto valueA = dynamic_cast(FindWindow(ExampleDialogWidgetsASpinner))->GetValue(); + auto valueB = dynamic_cast(FindWindow(ExampleDialogWidgetsBSpinner))->GetValue(); + dynamic_cast(FindWindow(ExampleDialogWidgetsGCDResult))->SetLabel(std::to_string(std::gcd(valueA, valueB))); + }; + spinA->Bind(wxEVT_SPINCTRL, action); + spinB->Bind(wxEVT_SPINCTRL, action); + // snippet withwxWidgets } // endsnippet withwxWidgets @@ -230,6 +264,7 @@ ExampleDialog::ExampleDialog(wxWindow* parent) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { using namespace wxUI; + // snippet wxUILayoutBasic // snippet wxUIGeneric VSizer { @@ -250,6 +285,20 @@ ExampleDialog::ExampleDialog(wxWindow* parent) .withSize(wxSize(200, 100)) // snippet withwxUI }, + HSizer { + Text { "A =" }, + a = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { + *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); + }), + + Text { "B =" }, + b = SpinCtrl { std::pair { 1, 10000 } } + .bind([this]() { *result = std::to_string(std::gcd(static_cast(*a), static_cast(*b))); }), + + Text { "GCD = " }, + result = Text { "1" }, + }, // endsnippet withwxUI RadioBox { "&Log Levels:", { "&Information", "&Warning", "&Error", "&None", "&Custom" } } .withStyle(wxRA_SPECIFY_ROWS) @@ -281,7 +330,7 @@ ExampleDialog::ExampleDialog(wxWindow* parent) // snippet withwxUI // snippet wxUILayoutBasic } - .attachTo(this); + .attachToAndFit(this); // endsnippet wxUIGeneric // endsnippet wxUILayoutBasic } diff --git a/include/wxUI/Bitmap.h b/include/wxUI/Bitmap.h index 2dcc2f2..699dd57 100644 --- a/include/wxUI/Bitmap.h +++ b/include/wxUI/Bitmap.h @@ -28,6 +28,7 @@ SOFTWARE. namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_static_bitmap.html struct Bitmap : public details::WidgetDetails { using super = details::WidgetDetails; @@ -58,6 +59,9 @@ struct Bitmap : public details::WidgetDetails { return new underlying_t(parent, getIdentity(), bitmap, getPos(), getSize(), getStyle()); } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + }; RULE_OF_SIX_BOILERPLATE(Bitmap); private: diff --git a/include/wxUI/BitmapButton.h b/include/wxUI/BitmapButton.h index 52ea23b..208fe50 100644 --- a/include/wxUI/BitmapButton.h +++ b/include/wxUI/BitmapButton.h @@ -28,6 +28,7 @@ SOFTWARE. namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_bitmap_button.html struct BitmapButton : public details::WidgetDetails { using super = details::WidgetDetails; @@ -74,6 +75,9 @@ struct BitmapButton : public details::WidgetDetails namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_bitmap_combo_box.html struct BitmapComboBox : public details::WidgetDetails { using super = details::WidgetDetails; @@ -106,6 +108,28 @@ struct BitmapComboBox : public details::WidgetDetails(controller->GetValue()); }, + [controller](std::string value) { controller->SetValue(value); } + }; + } + [[nodiscard]] auto selection() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetSelection(); }, + [controller](int selection) { controller->SetSelection(selection); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; RULE_OF_SIX_BOILERPLATE(BitmapComboBox); private: diff --git a/include/wxUI/BitmapToggleButton.h b/include/wxUI/BitmapToggleButton.h index 8a1d3c0..f923747 100644 --- a/include/wxUI/BitmapToggleButton.h +++ b/include/wxUI/BitmapToggleButton.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_bitmap_toggle_button.html struct BitmapToggleButton : public details::WidgetDetails { using super = details::WidgetDetails; @@ -70,6 +72,20 @@ struct BitmapToggleButton : public details::WidgetDetailsGetValue(); }, + [controller](bool value) { controller->SetValue(value); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; RULE_OF_SIX_BOILERPLATE(BitmapToggleButton); private: diff --git a/include/wxUI/Button.h b/include/wxUI/Button.h index 1ca66e0..b22a040 100644 --- a/include/wxUI/Button.h +++ b/include/wxUI/Button.h @@ -28,8 +28,10 @@ SOFTWARE. namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_button.html struct Button : public details::WidgetDetails { using super = details::WidgetDetails; + using details::WidgetDetails::underlying_t; explicit Button(wxWindowID identity, std::string text = "") : super(identity) @@ -74,6 +76,9 @@ struct Button : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_BUTTON, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + }; RULE_OF_SIX_BOILERPLATE(Button); private: diff --git a/include/wxUI/CheckBox.h b/include/wxUI/CheckBox.h index a11b180..3d7043e 100644 --- a/include/wxUI/CheckBox.h +++ b/include/wxUI/CheckBox.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_check_box.html struct CheckBox : public details::WidgetDetails { using super = details::WidgetDetails; @@ -72,6 +74,20 @@ struct CheckBox : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_CHECKBOX, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto value() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetValue(); }, + [controller](bool value) { controller->SetValue(value); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; RULE_OF_SIX_BOILERPLATE(CheckBox); private: diff --git a/include/wxUI/Choice.h b/include/wxUI/Choice.h index 91b9339..a283f2e 100644 --- a/include/wxUI/Choice.h +++ b/include/wxUI/Choice.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_choice.html struct Choice : public details::WidgetDetails { using super = details::WidgetDetails; @@ -72,6 +74,20 @@ struct Choice : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_CHOICE, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto selection() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetSelection(); }, + [controller](int selection) { controller->SetSelection(selection); } + }; + } + + auto operator->() const { return selection(); } + auto operator*() const { return selection(); } + }; RULE_OF_SIX_BOILERPLATE(Choice); private: diff --git a/include/wxUI/ComboBox.h b/include/wxUI/ComboBox.h index 35f6307..d830699 100644 --- a/include/wxUI/ComboBox.h +++ b/include/wxUI/ComboBox.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_combo_box.html struct ComboBox : public details::WidgetDetails { using super = details::WidgetDetails; @@ -73,6 +75,28 @@ struct ComboBox : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_COMBOBOX, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto value() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return static_cast(controller->GetValue()); }, + [controller](std::string value) { controller->SetValue(value); } + }; + } + [[nodiscard]] auto selection() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetSelection(); }, + [controller](int selection) { controller->SetSelection(selection); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; RULE_OF_SIX_BOILERPLATE(ComboBox); private: diff --git a/include/wxUI/GetterSetter.h b/include/wxUI/GetterSetter.h new file mode 100644 index 0000000..c42ff3a --- /dev/null +++ b/include/wxUI/GetterSetter.h @@ -0,0 +1,447 @@ +/* +MIT License + +Copyright (c) 2022 Richard Powell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once + +#include +#include +#include + +namespace wxUI::details { + +// why is this Enable here? Because we need to specialize on the type without over constraining. +template +struct GetterSetter { + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v>); + + explicit GetterSetter(Getter getter, Setter setter) + : getter(getter) + , setter(setter) + { + } + + [[nodiscard]] auto get() const -> Type + { + return getter(); + } + + // we specifically want implicit conversions for ergonomics + // NOLINTNEXTLINE (hicpp-explicit-conversions) + operator Type() const + { + return getter(); + } + + void set(Type const& value) + { + setter(value); + } + + auto operator=(Type const& value) -> GetterSetter& + { + setter(value); + return *this; + } + + auto operator->() -> GetterSetter* + { + return this; + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator<<(std::ostream& os, GetterSetter const& v) -> std::ostream& + { + return os << v.get(); + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator>>(std::istream& is, GetterSetter& v) -> std::istream& + { + auto temp = Type {}; + is >> temp; + v.set(std::move(temp)); + return is; + } + +private: + Getter getter; + Setter setter; +}; + +// CTAD for GetterSetter +template +GetterSetter(Getter, Setter) -> GetterSetter, Getter, Setter>; + +// Specialize for bool + +// This implemention supports logic operations for bools only +template +struct GetterSetter { + + using Type = bool; + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + explicit GetterSetter(Getter getter, Setter setter) + : getter(getter) + , setter(setter) + { + } + + [[nodiscard]] auto get() const -> Type + { + return getter(); + } + + // we specifically want implicit conversions for ergonomics + // NOLINTNEXTLINE (hicpp-explicit-conversions) + operator Type() const + { + return getter(); + } + + void set(Type value) + { + setter(value); + } + + auto operator=(Type value) -> GetterSetter& + { + setter(value); + return *this; + } + + auto operator&=(Type value) -> GetterSetter& + { + setter(getter() & value); + return *this; + } + + auto operator^=(Type value) -> GetterSetter& + { + setter(getter() ^ value); + return *this; + } + + auto operator|=(Type value) -> GetterSetter& + { + setter(getter() | value); + return *this; + } + + auto operator->() -> GetterSetter* + { + return this; + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator<<(std::ostream& os, GetterSetter const& v) -> std::ostream& + { + return os << v.get(); + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator>>(std::istream& is, GetterSetter& v) -> std::istream& + { + auto temp = Type {}; + is >> temp; + v.set(temp); + return is; + } + +private: + Getter getter; + Setter setter; +}; + +// Specialize for integral types +template +concept Integral = std::is_integral_v; + +template +struct GetterSetter { + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v>); + + explicit GetterSetter(Getter getter, Setter setter) + : getter(getter) + , setter(setter) + { + } + + [[nodiscard]] auto get() const -> Type + { + return getter(); + } + + // we specifically want implicit conversions for ergonomics + // NOLINTNEXTLINE (hicpp-explicit-conversions) + operator Type() const + { + return getter(); + } + + void set(Type value) + { + setter(value); + } + + auto operator=(Type value) -> GetterSetter& + { + setter(value); + return *this; + } + + auto operator+=(Type value) -> GetterSetter& + { + setter(getter() + value); + return *this; + } + + auto operator-=(Type value) -> GetterSetter& + { + setter(getter() - value); + return *this; + } + + auto operator*=(Type value) -> GetterSetter& + { + setter(getter() * value); + return *this; + } + + auto operator/=(Type value) -> GetterSetter& + { + setter(getter() / value); + return *this; + } + + auto operator%=(Type value) -> GetterSetter& + { + setter(getter() % value); + return *this; + } + + auto operator<<=(Type value) -> GetterSetter& + { + setter(getter() << value); + return *this; + } + + auto operator>>=(Type value) -> GetterSetter& + { + setter(getter() >> value); + return *this; + } + + auto operator&=(Type value) -> GetterSetter& + { + setter(getter() & value); + return *this; + } + + auto operator^=(Type value) -> GetterSetter& + { + setter(getter() ^ value); + return *this; + } + + auto operator|=(Type value) -> GetterSetter& + { + setter(getter() | value); + return *this; + } + + auto operator++() -> GetterSetter& + { + setter(getter() + 1); + return *this; + } + + auto operator++(int) -> Type + { + auto result = getter(); + setter(result + 1); + return result; + } + + auto operator--() -> GetterSetter& + { + setter(getter() - 1); + return *this; + } + + auto operator--(int) -> Type + { + auto result = getter(); + setter(result - 1); + return result; + } + + auto operator->() -> GetterSetter* + { + return this; + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator<<(std::ostream& os, GetterSetter const& v) -> std::ostream& + { + return os << v.get(); + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator>>(std::istream& is, GetterSetter& v) -> std::istream& + { + auto temp = Type {}; + is >> temp; + v.set(std::move(temp)); + return is; + } + +private: + Getter getter; + Setter setter; +}; + +// Specialize for Float types +template +concept FloatingPoint = std::is_floating_point_v; + +template +struct GetterSetter { + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v>); + + explicit GetterSetter(Getter getter, Setter setter) + : getter(getter) + , setter(setter) + { + } + + [[nodiscard]] auto get() const -> Type + { + return getter(); + } + + // we specifically want implicit conversions for ergonomics + // NOLINTNEXTLINE (hicpp-explicit-conversions) + operator Type() const + { + return getter(); + } + + void set(Type value) + { + setter(value); + } + + auto operator=(Type value) -> GetterSetter& + { + setter(value); + return *this; + } + + auto operator+=(Type value) -> GetterSetter& + { + setter(getter() + value); + return *this; + } + + auto operator-=(Type value) -> GetterSetter& + { + setter(getter() - value); + return *this; + } + + auto operator*=(Type value) -> GetterSetter& + { + setter(getter() * value); + return *this; + } + + auto operator/=(Type value) -> GetterSetter& + { + setter(getter() / value); + return *this; + } + + auto operator++() -> GetterSetter& + { + setter(getter() + 1); + return *this; + } + + auto operator++(int) -> Type + { + auto result = getter(); + setter(result + 1); + return result; + } + + auto operator--() -> GetterSetter& + { + setter(getter() - 1); + return *this; + } + + auto operator--(int) -> Type + { + auto result = getter(); + setter(result - 1); + return result; + } + + auto operator->() -> GetterSetter* + { + return this; + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator<<(std::ostream& os, GetterSetter const& v) -> std::ostream& + { + return os << v.get(); + } + + // NOLINTNEXTLINE (readability-identifier-length) + friend auto operator>>(std::istream& is, GetterSetter& v) -> std::istream& + { + auto temp = Type {}; + is >> temp; + v.set(std::move(temp)); + return is; + } + +private: + Getter getter; + Setter setter; +}; + +} diff --git a/include/wxUI/Hyperlink.h b/include/wxUI/Hyperlink.h index fc8f431..9f45a9a 100644 --- a/include/wxUI/Hyperlink.h +++ b/include/wxUI/Hyperlink.h @@ -28,6 +28,7 @@ SOFTWARE. namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_hyperlink_ctrl.html struct Hyperlink : public details::WidgetDetails { using super = details::WidgetDetails; @@ -60,6 +61,9 @@ struct Hyperlink : public details::WidgetDetails { return new underlying_t(parent, getIdentity(), text, url, getPos(), getSize(), getStyle()); } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + }; RULE_OF_SIX_BOILERPLATE(Hyperlink); private: diff --git a/include/wxUI/Line.h b/include/wxUI/Line.h index 3de1226..903d48d 100644 --- a/include/wxUI/Line.h +++ b/include/wxUI/Line.h @@ -28,6 +28,7 @@ SOFTWARE. namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_static_line.html struct Line : public details::WidgetDetails { using super = details::WidgetDetails; @@ -46,6 +47,9 @@ struct Line : public details::WidgetDetails { return new underlying_t(parent, getIdentity(), getPos(), getSize(), getStyle()); } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + }; RULE_OF_SIX_BOILERPLATE(Line); }; diff --git a/include/wxUI/ListBox.h b/include/wxUI/ListBox.h index 03c0ae4..c07df99 100644 --- a/include/wxUI/ListBox.h +++ b/include/wxUI/ListBox.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_list_box.html struct ListBox : public details::WidgetDetails { using super = details::WidgetDetails; @@ -72,6 +74,20 @@ struct ListBox : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_LISTBOX, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto selection() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetSelection(); }, + [controller](int selection) { controller->SetSelection(selection); } + }; + } + + auto operator->() const { return selection(); } + auto operator*() const { return selection(); } + }; RULE_OF_SIX_BOILERPLATE(ListBox); private: diff --git a/include/wxUI/RadioBox.h b/include/wxUI/RadioBox.h index bb75888..e71551f 100644 --- a/include/wxUI/RadioBox.h +++ b/include/wxUI/RadioBox.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_radio_box.html struct RadioBox : details::WidgetDetails { using super = details::WidgetDetails; @@ -100,6 +102,20 @@ struct RadioBox : details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_RADIOBOX, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto selection() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetSelection(); }, + [controller](int selection) { controller->SetSelection(selection); } + }; + } + + auto operator->() const { return selection(); } + auto operator*() const { return selection(); } + }; RULE_OF_SIX_BOILERPLATE(RadioBox); private: diff --git a/include/wxUI/Slider.h b/include/wxUI/Slider.h index 2b0f3fe..74d954b 100644 --- a/include/wxUI/Slider.h +++ b/include/wxUI/Slider.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_slider.html struct Slider : public details::WidgetDetails { using super = details::WidgetDetails; @@ -70,6 +72,20 @@ struct Slider : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_SLIDER, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + [[nodiscard]] auto value() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetValue(); }, + [controller](int value) { controller->SetValue(value); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; RULE_OF_SIX_BOILERPLATE(Slider); private: diff --git a/include/wxUI/SpinCtrl.h b/include/wxUI/SpinCtrl.h index 614ebb1..7e5de99 100644 --- a/include/wxUI/SpinCtrl.h +++ b/include/wxUI/SpinCtrl.h @@ -23,17 +23,19 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_spin_ctrl.html struct SpinCtrl : public details::WidgetDetails { using super = details::WidgetDetails; explicit SpinCtrl(wxWindowID identity, std::optional> range = {}, std::optional initial = {}) : super(identity) - , range(range) + , range(std::move(range)) , initial(initial) { } @@ -45,7 +47,7 @@ struct SpinCtrl : public details::WidgetDetails { explicit SpinCtrl(wxSizerFlags const& flags, wxWindowID identity, std::optional> range = {}, std::optional initial = {}) : super(flags, identity) - , range(range) + , range(std::move(range)) , initial(initial) { } @@ -55,13 +57,12 @@ struct SpinCtrl : public details::WidgetDetails { { } - auto create(wxWindow* parent) -> wxWindow* override + auto create(wxWindow* parent) -> wxWindow* { auto min = range ? range->first : 0; auto max = range ? range->second : 100; auto initvalue = initial ? *initial : min; - auto* widget = new underlying_t(parent, getIdentity(), wxEmptyString, getPos(), getSize(), getStyle(), min, max, initvalue); - return widget; + return setProxy(new underlying_t(parent, getIdentity(), wxEmptyString, getPos(), getSize(), getStyle(), min, max, initvalue)); } template @@ -70,6 +71,22 @@ struct SpinCtrl : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_SPINCTRL, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + + [[nodiscard]] auto value() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return controller->GetValue(); }, + [controller](int value) { controller->SetValue(value); } + }; + } + + auto operator->() const { return value(); } + auto operator*() const { return value(); } + }; + RULE_OF_SIX_BOILERPLATE(SpinCtrl); private: diff --git a/include/wxUI/Text.h b/include/wxUI/Text.h index 70e00b8..058747c 100644 --- a/include/wxUI/Text.h +++ b/include/wxUI/Text.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_text.html struct Text : public details::WidgetDetails { using super = details::WidgetDetails; @@ -57,9 +59,25 @@ struct Text : public details::WidgetDetails { auto create(wxWindow* parent) -> wxWindow* override { - return new underlying_t(parent, getIdentity(), text, getPos(), getSize(), getStyle()); + return setProxy(new underlying_t(parent, getIdentity(), text, getPos(), getSize(), getStyle())); } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + + [[nodiscard]] auto label() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return static_cast(controller->GetLabel()); }, + [controller](std::string label) { controller->SetLabel(label); } + }; + } + + auto operator->() const { return label(); } + auto operator*() const { return label(); } + }; + RULE_OF_SIX_BOILERPLATE(Text); private: diff --git a/include/wxUI/TextCtrl.h b/include/wxUI/TextCtrl.h index fe15570..0bc98c1 100644 --- a/include/wxUI/TextCtrl.h +++ b/include/wxUI/TextCtrl.h @@ -23,11 +23,13 @@ SOFTWARE. */ #pragma once +#include "GetterSetter.h" #include "Widget.h" #include namespace wxUI { +// https://docs.wxwidgets.org/latest/classwx_text_ctrl.html struct TextCtrl : public details::WidgetDetails { using super = details::WidgetDetails; @@ -64,6 +66,22 @@ struct TextCtrl : public details::WidgetDetails { return details::BindWidgetToEvent { *this, wxEVT_TEXT, func }; } + struct Proxy : super::WidgetProxy { + PROXY_BOILERPLATE(); + + [[nodiscard]] auto label() const + { + auto* controller = control(); + return details::GetterSetter { + [controller] { return static_cast(controller->GetLabel()); }, + [controller](std::string label) { controller->SetLabel(label); } + }; + } + + auto operator->() const { return label(); } + auto operator*() const { return label(); } + }; + RULE_OF_SIX_BOILERPLATE(TextCtrl); private: diff --git a/include/wxUI/Widget.h b/include/wxUI/Widget.h index 4607f30..b895f2a 100644 --- a/include/wxUI/Widget.h +++ b/include/wxUI/Widget.h @@ -73,13 +73,11 @@ template overloaded(Ts...) -> overloaded; template -struct BindWidgetToEvent { - W widget; - Event event; - Function function; +struct BindWidgetToEvent : W { + using super = W; BindWidgetToEvent(W const& widget, Event const& event, Function const& function) - : widget(widget) + : W(widget) , event(event) , function(function) { @@ -87,7 +85,7 @@ struct BindWidgetToEvent { auto createAndAdd(wxWindow* parent, wxSizer* sizer, wxSizerFlags const& flags) { - wxWindow* window = widget.createAndAdd(parent, sizer, flags); + auto* window = super::createAndAdd(parent, sizer, flags); if constexpr (is_noarg_callable()) { window->Bind(event, [function = function](auto) { function(); @@ -97,15 +95,19 @@ struct BindWidgetToEvent { } return window; } -}; -template -BindWidgetToEvent(W, Event, Function) -> BindWidgetToEvent; +private: + Event event; + Function function; +}; // This is to allow disambiguation of construction with a style struct withStyle { }; +template +struct WidgetProxy; + // The WidgetDetails are the base class of the Controllers. The common details // across many controllers are stored in the base class. // The "recipe" for constructing a controller is pretty straight forward: @@ -114,7 +116,27 @@ struct withStyle { // 3. implement the create function for constructing the concrete widget. template struct WidgetDetails { + using Controller = ConcreteWidget; using underlying_t = Underlying; + + struct WidgetProxy { + [[nodiscard]] auto control() const -> Underlying* + { + if (!controller) { + throw std::runtime_error("Proxy class has not been attached"); + } + return controller; + } + + void setUnderlying(Underlying* control) + { + controller = control; + } + + private: + Underlying* controller {}; + }; + explicit WidgetDetails(wxWindowID identity = wxID_ANY) : identity(identity) { @@ -145,12 +167,18 @@ struct WidgetDetails { return static_cast(*this); } - auto withSize(wxSize size_) -> ConcreteWidget& + auto withSize(wxSize size_) & -> ConcreteWidget& { size = size_; return static_cast(*this); } + auto withSize(wxSize size_) && -> ConcreteWidget&& + { + size = size_; + return static_cast(*this); + } + auto withStyle(int64_t style_) -> ConcreteWidget& { style = style_; @@ -163,22 +191,13 @@ struct WidgetDetails { return static_cast(*this); } - auto getHandle(Underlying** handle) -> ConcreteWidget& - { - windowHandle = handle; - return static_cast(*this); - } - - auto createAndAdd(wxWindow* parent, wxSizer* sizer, wxSizerFlags const& parentFlags) + auto createAndAdd(wxWindow* parent, wxSizer* sizer, wxSizerFlags const& parentFlags) -> Underlying* { auto widget = dynamic_cast(create(parent)); if (fontInfo) { widget->SetFont(wxFont(*fontInfo)); } sizer->Add(widget, flags ? *flags : parentFlags); - if (windowHandle) { - *windowHandle = widget; - } return widget; } @@ -187,6 +206,20 @@ struct WidgetDetails { auto getSize() const { return size; } auto getStyle() const { return style; } + void setProxyHandle(WidgetProxy* proxy) + { + proxyHandle = proxy; + } + +protected: + auto setProxy(Underlying* widget) -> Underlying* + { + if (proxyHandle) { + (*proxyHandle)->setUnderlying(widget); + } + return widget; + } + private: // these should be implemented in the derived classes. // aka the Template Pattern @@ -199,7 +232,7 @@ struct WidgetDetails { wxSize size = wxDefaultSize; int64_t style {}; std::optional fontInfo {}; - Underlying** windowHandle {}; + std::optional proxyHandle {}; }; #define RULE_OF_SIX_BOILERPLATE(WIDGET) \ @@ -209,4 +242,14 @@ struct WidgetDetails { auto operator=(WIDGET const&)->WIDGET& = default; \ auto operator=(WIDGET&&)->WIDGET& = default; +#define PROXY_BOILERPLATE() \ + template \ + auto operator=(W&& controller)->W&& { return bind(std::forward(controller)); } \ + template \ + auto bind(W&& widget)->W&& \ + { \ + widget.setProxyHandle(this); \ + return std::forward(widget); \ + } + } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec9d57e..79c06d4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(wxUI_Tests ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_CheckBoxTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_ChoiceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_ComboBoxTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_GetterSetterTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_HyperlinkTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_LayoutTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wxUI_LineTests.cpp diff --git a/tests/wxUI_BitmapButtonTests.cpp b/tests/wxUI_BitmapButtonTests.cpp index 713fbe8..e8bc1e8 100644 --- a/tests/wxUI_BitmapButtonTests.cpp +++ b/tests/wxUI_BitmapButtonTests.cpp @@ -24,16 +24,31 @@ SOFTWARE. #include #include +#include "wxUI_TestControlCommon.h" + #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::BitmapButton; + +struct BitmapButtonTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { wxBitmap {} }; } + static auto testStyle() { return (wxBU_LEFT | wxBU_EXACTFIT | wxBU_NOTEXT); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return BitmapButtonTestPolicy::createUUT(); } + TEST_CASE("BitmapButton") { SECTION("bitmap") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -67,7 +82,7 @@ TEST_CASE("BitmapButton") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }.withStyle(wxBU_LEFT); + auto uut = createUUT().withStyle(wxBU_LEFT); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxBU_LEFT | wxBU_EXACTFIT | wxBU_NOTEXT)); } @@ -75,7 +90,7 @@ TEST_CASE("BitmapButton") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -83,9 +98,11 @@ TEST_CASE("BitmapButton") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(BitmapButtonTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_BitmapComboBoxTests.cpp b/tests/wxUI_BitmapComboBoxTests.cpp index efe05ae..b890416 100644 --- a/tests/wxUI_BitmapComboBoxTests.cpp +++ b/tests/wxUI_BitmapComboBoxTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::BitmapComboBox; + +struct BitmapComboBoxTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { { { wxString {}, wxBitmap {} } } }; } + static auto testStyle() { return (wxCB_SORT); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return BitmapComboBoxTestPolicy::createUUT(); } + TEST_CASE("BitmapComboBox") { SECTION("bitmap") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { { wxString {}, wxBitmap {} } } }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -67,7 +81,7 @@ TEST_CASE("BitmapComboBox") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { { wxString {}, wxBitmap {} } } }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -75,7 +89,7 @@ TEST_CASE("BitmapComboBox") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { { wxString {}, wxBitmap {} } } }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } @@ -91,5 +105,7 @@ TEST_CASE("BitmapComboBox") CHECK("Hello" == window->GetString(0)); CHECK("Goodbye" == window->GetString(1)); } + + CHAINING_TEST(BitmapComboBoxTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_BitmapTests.cpp b/tests/wxUI_BitmapTests.cpp index d033930..ae0aed8 100644 --- a/tests/wxUI_BitmapTests.cpp +++ b/tests/wxUI_BitmapTests.cpp @@ -21,20 +21,35 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Bitmap; +struct BitmapTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { wxBitmap {} }; } + static auto testStyle() { return (0); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return BitmapTestPolicy::createUUT(); } + TEST_CASE("Bitmap") { SECTION("bitmap") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }; + auto uut = createUUT(); + auto* window = dynamic_cast(uut.create(&frame)); + CHECK(window != nullptr); } SECTION("id.bitmap") @@ -60,5 +75,7 @@ TEST_CASE("Bitmap") auto* window = dynamic_cast(uut.create(&frame)); CHECK(10000 == window->GetId()); } + + CHAINING_TEST(BitmapTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_BitmapToggleButtonTests.cpp b/tests/wxUI_BitmapToggleButtonTests.cpp index a4db523..4d12cb2 100644 --- a/tests/wxUI_BitmapToggleButtonTests.cpp +++ b/tests/wxUI_BitmapToggleButtonTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::BitmapToggleButton; + +struct BitmapToggleButtonTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { wxBitmap {} }; } + static auto testStyle() { return (wxBU_LEFT | wxBU_EXACTFIT | wxBU_NOTEXT); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return BitmapToggleButtonTestPolicy::createUUT(); } + TEST_CASE("BitmapToggleButton") { SECTION("bitmap") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -101,7 +115,7 @@ TEST_CASE("BitmapToggleButton") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -109,9 +123,11 @@ TEST_CASE("BitmapToggleButton") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { wxBitmap {} }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(BitmapToggleButtonTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_ButtonTests.cpp b/tests/wxUI_ButtonTests.cpp index 1114e6d..3784acb 100644 --- a/tests/wxUI_ButtonTests.cpp +++ b/tests/wxUI_ButtonTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Button; + +struct ButtonTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return (wxBU_LEFT | wxBU_EXACTFIT | wxBU_NOTEXT); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return ButtonTestPolicy::createUUT(); } + TEST_CASE("Button") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); uut.withPosition(wxPoint {}); @@ -41,7 +55,7 @@ TEST_CASE("Button") SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -109,7 +123,7 @@ TEST_CASE("Button") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxBU_LEFT); + auto uut = createUUT().withStyle(wxBU_LEFT); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == wxBU_LEFT); } @@ -117,7 +131,7 @@ TEST_CASE("Button") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -125,9 +139,11 @@ TEST_CASE("Button") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(ButtonTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_CheckBoxTests.cpp b/tests/wxUI_CheckBoxTests.cpp index 61be350..246ba29 100644 --- a/tests/wxUI_CheckBoxTests.cpp +++ b/tests/wxUI_CheckBoxTests.cpp @@ -21,27 +21,40 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::CheckBox; +struct CheckBoxTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return (wxCHK_3STATE); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return CheckBoxTestPolicy::createUUT(); } + TEST_CASE("CheckBox") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -109,7 +122,7 @@ TEST_CASE("CheckBox") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxCHK_3STATE); + auto uut = createUUT().withStyle(wxCHK_3STATE); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == wxCHK_3STATE); } @@ -117,7 +130,7 @@ TEST_CASE("CheckBox") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -125,9 +138,11 @@ TEST_CASE("CheckBox") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(CheckBoxTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_ChoiceTests.cpp b/tests/wxUI_ChoiceTests.cpp index 1b63e24..6dc4ec1 100644 --- a/tests/wxUI_ChoiceTests.cpp +++ b/tests/wxUI_ChoiceTests.cpp @@ -21,27 +21,40 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Choice; +struct ChoiceTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return (wxCB_SORT); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return ChoiceTestPolicy::createUUT(); } + TEST_CASE("Choice") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(0 == window->GetCount()); } SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(0 == window->GetCount()); } @@ -133,7 +146,7 @@ TEST_CASE("Choice") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxCB_SORT); + auto uut = createUUT().withStyle(wxCB_SORT); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == wxCB_SORT); } @@ -141,7 +154,7 @@ TEST_CASE("Choice") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -149,9 +162,11 @@ TEST_CASE("Choice") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(ChoiceTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_ComboBoxTests.cpp b/tests/wxUI_ComboBoxTests.cpp index b545ace..cb38661 100644 --- a/tests/wxUI_ComboBoxTests.cpp +++ b/tests/wxUI_ComboBoxTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::ComboBox; + +struct ComboBoxTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { { wxString {} } }; } + static auto testStyle() { return (wxCB_DROPDOWN | wxCB_READONLY); } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return ComboBoxTestPolicy::createUUT(); } + TEST_CASE("ComboBox") { SECTION("choice") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { wxString {} } }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -67,7 +81,7 @@ TEST_CASE("ComboBox") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { wxString {} } }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -75,7 +89,7 @@ TEST_CASE("ComboBox") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { { wxString {} } }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } @@ -91,5 +105,7 @@ TEST_CASE("ComboBox") CHECK("Hello" == window->GetString(0)); CHECK("Goodbye" == window->GetString(1)); } + + CHAINING_TEST(ComboBoxTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_GetterSetterTests.cpp b/tests/wxUI_GetterSetterTests.cpp new file mode 100644 index 0000000..59f1194 --- /dev/null +++ b/tests/wxUI_GetterSetterTests.cpp @@ -0,0 +1,600 @@ +/* +MIT License + +Copyright (c) 2022 Richard Powell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include +#include +#include + +#include + +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, cppcoreguidelines-avoid-do-while, readability-magic-numbers, readability-function-cognitive-complexity, hicpp-function-size, misc-use-anonymous-namespace) +#define BASIC_CHECK() \ + do { \ + CHECK(value == checkValue); \ + CHECK(getterCallCount == checkGetterCallCount); \ + CHECK(setterCallCount == checkSetterCallCount); \ + } while (0) + +TEST_CASE("GetterSetter") +{ + SECTION("basics") + { + auto getterCallCount = int {}; + auto setterCallCount = int {}; + auto value = int {}; + + auto uut = wxUI::details::GetterSetter { + [&] { + ++getterCallCount; + return value; + }, + [&](int v) { + ++setterCallCount; + value = v; + } + }; + // Basic check + CHECK(value == 0); + CHECK(getterCallCount == 0); + CHECK(setterCallCount == 0); + + CHECK(0 == uut.get()); + + CHECK(value == 0); + CHECK(getterCallCount == 1); + CHECK(setterCallCount == 0); + + uut.set(1); + + CHECK(value == 1); + CHECK(getterCallCount == 1); + CHECK(setterCallCount == 1); + + // Operator check + CHECK(1 == static_cast(uut)); + + CHECK(value == 1); + CHECK(getterCallCount == 2); + CHECK(setterCallCount == 1); + + uut = 2; + + CHECK(value == 2); + CHECK(getterCallCount == 2); + CHECK(setterCallCount == 2); + + auto ostream = std::ostringstream {}; + ostream << uut; + }; + + SECTION("strings") + { + auto setterCallCount = int {}; + auto getterCallCount = int {}; + auto value = std::string { "hello" }; + + auto checkGetterCallCount = int {}; + auto checkSetterCallCount = int {}; + auto checkValue = std::string { "hello" }; + + auto uut = wxUI::details::GetterSetter { + [&] { + ++getterCallCount; + return value; + }, + [&](std::string v) { + ++setterCallCount; + value = v; + } + }; + + BASIC_CHECK(); + + CHECK("hello" == uut.get()); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + // << and >> + auto ostream = std::ostringstream {}; + ostream << uut; + checkGetterCallCount += 1; + + CHECK(ostream.str() == "hello"); + + BASIC_CHECK(); + + auto istream = std::istringstream { "test1\ntest2" }; + istream >> uut; + checkSetterCallCount += 1; + checkValue = "test1"; + + BASIC_CHECK(); + }; + + SECTION("ints") + { + auto getterCallCount = int {}; + auto setterCallCount = int {}; + auto value = int {}; + + auto checkGetterCallCount = int {}; + auto checkSetterCallCount = int {}; + auto checkValue = int {}; + + auto uut = wxUI::details::GetterSetter { + [&] { + ++getterCallCount; + return value; + }, + [&](int v) { + ++setterCallCount; + value = v; + } + }; + BASIC_CHECK(); + + CHECK(0 == uut.get()); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + uut.set(1); + checkSetterCallCount += 1; + checkValue = 1; + + BASIC_CHECK(); + + // Operator check + CHECK(1 == static_cast(uut)); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + uut = 2; + checkSetterCallCount += 1; + checkValue = 2; + + BASIC_CHECK(); + + // comparisions + CHECK(2 == uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(1 != uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(3 > uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(2 >= uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(1 < uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(2 <= uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut == 2); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut != 1); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut < 3); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut <= 2); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut > 1); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut >= 2); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + // compound assignments + uut += 1; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue += 1; + + BASIC_CHECK(); + + uut -= 1; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue -= 1; + + BASIC_CHECK(); + + uut *= 2; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue *= 2; + + BASIC_CHECK(); + + uut /= 2; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue /= 2; + + BASIC_CHECK(); + + value = 10; + uut %= 7; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 3; + + BASIC_CHECK(); + + value = 2; + uut <<= 2; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 8; + + BASIC_CHECK(); + + uut >>= 2; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 2; + + BASIC_CHECK(); + + uut |= 4; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 6; + + BASIC_CHECK(); + + uut &= 4; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 4; + + BASIC_CHECK(); + + uut ^= 6; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 2; + + BASIC_CHECK(); + + // chaining + value = 3; + auto t = 2 + (uut = 5); + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 5; + + CHECK(t == 7); + BASIC_CHECK(); + + // increment decrement + value = 3; + t = uut++; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 4; + + CHECK(t == 3); + BASIC_CHECK(); + + t = ++uut; + // this increments by 2 because there's the getter for ++ and then for the assignment to t + checkGetterCallCount += 2; + checkSetterCallCount += 1; + checkValue = 5; + + CHECK(t == 5); + BASIC_CHECK(); + + t = uut--; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = 4; + + CHECK(t == 5); + BASIC_CHECK(); + t = --uut; + // this increments by 2 because there's the getter for -- and then for the assignment to t + checkGetterCallCount += 2; + checkSetterCallCount += 1; + checkValue = 3; + + CHECK(t == 3); + BASIC_CHECK(); + + // << and >> + auto ostream = std::ostringstream {}; + ostream << uut; + checkGetterCallCount += 1; + + CHECK(ostream.str() == "3"); + BASIC_CHECK(); + + auto istream = std::istringstream { "2\n3.5" }; + istream >> uut; + checkSetterCallCount += 1; + checkValue = 2; + + BASIC_CHECK(); + + istream >> uut; + checkSetterCallCount += 1; + checkValue = 3; + + BASIC_CHECK(); + }; + + SECTION("bools") + { + auto getterCallCount = int {}; + auto setterCallCount = int {}; + auto value = bool {}; + + auto checkGetterCallCount = int {}; + auto checkSetterCallCount = int {}; + auto checkValue = bool {}; + + auto uut = wxUI::details::GetterSetter { + [&] { + ++getterCallCount; + return value; + }, + [&](bool v) { + ++setterCallCount; + value = v; + } + }; + BASIC_CHECK(); + + CHECK(false == uut.get()); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + uut.set(true); + checkSetterCallCount += 1; + checkValue = 1; + + BASIC_CHECK(); + + // Operator check + CHECK(true == static_cast(uut)); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + uut = false; + checkSetterCallCount += 1; + checkValue = false; + + BASIC_CHECK(); + + // comparisions + CHECK(false == uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(true != uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(true > uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(false >= uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + value = true; + CHECK(false < uut); + checkGetterCallCount += 1; + checkValue = true; + + BASIC_CHECK(); + + CHECK(true <= uut); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut == true); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + CHECK(uut != false); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + value = false; + CHECK(uut < true); + checkGetterCallCount += 1; + checkValue = false; + + BASIC_CHECK(); + + CHECK(uut <= false); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + value = true; + CHECK(uut > false); + checkGetterCallCount += 1; + checkValue = true; + + BASIC_CHECK(); + + CHECK(uut >= true); + checkGetterCallCount += 1; + + BASIC_CHECK(); + + // compound assignments + value = true; + uut |= true; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = true; + + BASIC_CHECK(); + + uut &= false; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = false; + + BASIC_CHECK(); + + uut ^= true; + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = true; + + BASIC_CHECK(); + + // chaining + value = true; + auto t = true && (uut = false); + checkGetterCallCount += 1; + checkSetterCallCount += 1; + checkValue = false; + + CHECK(t == false); + BASIC_CHECK(); + + // << and >> + auto ostream = std::ostringstream {}; + ostream << uut; + checkGetterCallCount += 1; + + CHECK(ostream.str() == "0"); + BASIC_CHECK(); + + auto istream = std::istringstream { "1\n0" }; + istream >> uut; + checkSetterCallCount += 1; + checkValue = true; + + BASIC_CHECK(); + + istream >> uut; + checkSetterCallCount += 1; + checkValue = false; + + BASIC_CHECK(); + }; + + SECTION("floats") + { + auto getterCallCount = int {}; + auto setterCallCount = int {}; + auto value = float {}; + + auto checkGetterCallCount = int {}; + auto checkSetterCallCount = int {}; + auto checkValue = float {}; + + auto uut = wxUI::details::GetterSetter { + [&] { + ++getterCallCount; + return value; + }, + [&](float v) { + ++setterCallCount; + value = v; + } + }; + // Basic check + BASIC_CHECK(); + + uut = 2.5; + checkSetterCallCount += 1; + checkValue = 2.5; + + BASIC_CHECK(); + + // << and >> + auto ostream = std::ostringstream {}; + ostream << uut; + checkGetterCallCount += 1; + + CHECK(ostream.str() == "2.5"); + BASIC_CHECK(); + + auto istream = std::istringstream { "2\n3.5" }; + istream >> uut; + checkSetterCallCount += 1; + checkValue = 2.0; + + BASIC_CHECK(); + + istream >> uut; + checkSetterCallCount += 1; + checkValue = 3.5; + + BASIC_CHECK(); + } + // put all the negative tests here. +} +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, cppcoreguidelines-avoid-do-while, readability-magic-numbers, readability-function-cognitive-complexity, hicpp-function-size, misc-use-anonymous-namespace) diff --git a/tests/wxUI_HyperlinkTests.cpp b/tests/wxUI_HyperlinkTests.cpp index ab83245..53fe466 100644 --- a/tests/wxUI_HyperlinkTests.cpp +++ b/tests/wxUI_HyperlinkTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Hyperlink; + +struct HyperlinkTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { "Hello", "www.github.com" }; } + static auto testStyle() { return wxHL_CONTEXTMENU; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return HyperlinkTestPolicy::createUUT(); } + TEST_CASE("Hyperlink") { SECTION("name.url") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { "Hello", "www.github.com" }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK("Hello" == window->GetLabel()); CHECK("www.github.com" == window->GetURL()); @@ -71,7 +85,7 @@ TEST_CASE("Hyperlink") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { "Hello", "www.github.com" }.withStyle(wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU); + auto uut = createUUT().withStyle(wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU)); } @@ -79,7 +93,7 @@ TEST_CASE("Hyperlink") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { "Hello", "www.github.com" }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -87,9 +101,11 @@ TEST_CASE("Hyperlink") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { "Hello", "www.github.com" }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(HyperlinkTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_LineTests.cpp b/tests/wxUI_LineTests.cpp index 8907fa6..b1e433c 100644 --- a/tests/wxUI_LineTests.cpp +++ b/tests/wxUI_LineTests.cpp @@ -21,20 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Line; +struct LineTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxLI_VERTICAL; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return LineTestPolicy::createUUT(); } + TEST_CASE("Line") { SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window != nullptr); } @@ -62,5 +75,7 @@ TEST_CASE("Line") auto* window = dynamic_cast(uut.create(&frame)); CHECK(10000 == window->GetId()); } + + CHAINING_TEST(LineTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_ListBoxTests.cpp b/tests/wxUI_ListBoxTests.cpp index d225025..4d3b5c4 100644 --- a/tests/wxUI_ListBoxTests.cpp +++ b/tests/wxUI_ListBoxTests.cpp @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include @@ -29,19 +30,31 @@ SOFTWARE. // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) using TypeUnderTest = wxUI::ListBox; +struct ListBoxTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxLB_ALWAYS_SB | wxLB_MULTIPLE; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return ListBoxTestPolicy::createUUT(); } + TEST_CASE("ListBox") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(0 == window->GetCount()); } SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(0 == window->GetCount()); } @@ -133,7 +146,7 @@ TEST_CASE("ListBox") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxLB_MULTIPLE); + auto uut = createUUT().withStyle(wxLB_MULTIPLE); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxBORDER_SUNKEN | wxLB_MULTIPLE)); } @@ -141,9 +154,11 @@ TEST_CASE("ListBox") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } + + CHAINING_TEST(ListBoxTestPolicy) } // NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) diff --git a/tests/wxUI_RadioBoxTests.cpp b/tests/wxUI_RadioBoxTests.cpp index b6aadfc..d35ed94 100644 --- a/tests/wxUI_RadioBoxTests.cpp +++ b/tests/wxUI_RadioBoxTests.cpp @@ -21,20 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::RadioBox; +struct RadioBoxTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest { std::vector { "Hello", "Goodbye" } }; } + static auto testStyle() { return wxRA_SPECIFY_COLS; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return RadioBoxTestPolicy::createUUT(); } + TEST_CASE("RadioBox") { SECTION("choices") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { std::vector { "Hello", "Goodbye" } }; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); CHECK(2 == window->GetCount()); @@ -134,7 +147,7 @@ TEST_CASE("RadioBox") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { std::vector { "Hello", "Goodbye" } }.withStyle(wxRA_SPECIFY_COLS); + auto uut = createUUT().withStyle(wxRA_SPECIFY_COLS); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxRA_SPECIFY_COLS | wxTAB_TRAVERSAL)); } @@ -142,7 +155,7 @@ TEST_CASE("RadioBox") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { std::vector { "Hello", "Goodbye" } }.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -150,9 +163,11 @@ TEST_CASE("RadioBox") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest { std::vector { "Hello", "Goodbye" } }.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(RadioBoxTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_SliderTests.cpp b/tests/wxUI_SliderTests.cpp index fe777e3..b001e6a 100644 --- a/tests/wxUI_SliderTests.cpp +++ b/tests/wxUI_SliderTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Slider; + +struct SliderTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxSL_LABELS; } + static auto testPosition() { return wxPoint { 14, 2 }; } + static auto testSize() { return wxSize { 100, 40 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return wxPoint { 18, 2 }; } + static auto expectedSize() { return wxSize { 85, 40 }; } +}; +static auto createUUT() { return SliderTestPolicy::createUUT(); } + TEST_CASE("Slider") { SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetValue() == 0); } @@ -131,5 +145,7 @@ TEST_CASE("Slider") CHECK(10000 == window->GetId()); CHECK(window->GetValue() == 3); } + + CHAINING_TEST(SliderTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_SpinCtrlTests.cpp b/tests/wxUI_SpinCtrlTests.cpp index 7e68f6c..5ba102f 100644 --- a/tests/wxUI_SpinCtrlTests.cpp +++ b/tests/wxUI_SpinCtrlTests.cpp @@ -21,19 +21,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::SpinCtrl; + +struct SpinCtrlTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxTE_PROCESS_ENTER; } + static auto testPosition() { return wxPoint { 5, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return SpinCtrlTestPolicy::createUUT(); } + TEST_CASE("SpinCtrl") { SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetValue() == 0); } @@ -135,7 +149,7 @@ TEST_CASE("SpinCtrl") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxTE_PROCESS_ENTER); + auto uut = createUUT().withStyle(wxTE_PROCESS_ENTER); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxTE_PROCESS_ENTER | wxTAB_TRAVERSAL | 0x200000)); } @@ -143,7 +157,7 @@ TEST_CASE("SpinCtrl") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -151,9 +165,27 @@ TEST_CASE("SpinCtrl") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + SECTION("proxy") + { + wxFrame frame { nullptr, wxID_ANY, "" }; + auto proxy = TypeUnderTest::Proxy {}; + auto uut = proxy = createUUT().withSize({ 1, 2 }); + uut.create(&frame); + + CHECK(proxy->get() == 0); + proxy->set(1); + CHECK(proxy->get() == 1); + *proxy = 2; + CHECK(proxy->get() == 2); + int result2 = *proxy; + CHECK(result2 == 2); + } + + CHAINING_TEST(SpinCtrlTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_TestControlCommon.h b/tests/wxUI_TestControlCommon.h new file mode 100644 index 0000000..fb156a2 --- /dev/null +++ b/tests/wxUI_TestControlCommon.h @@ -0,0 +1,103 @@ +/* +MIT License + +Copyright (c) 2022 Richard Powell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) + +constexpr auto addWithStyle = [](auto style, auto&& inUUT) { + return inUUT.withStyle(style); +}; + +constexpr auto addWithPosition = [](auto pos, auto&& inUUT) { + return inUUT.withPosition(pos); +}; + +constexpr auto addWithSize = [](auto size, auto&& inUUT) { + return inUUT.withSize(size); +}; + +constexpr auto checkWithStyle = [](auto style, auto* window) { + CHECK((window->GetWindowStyle() & style) == style); + return window; +}; + +constexpr auto checkWithPosition = [](auto pos, auto* window) { + CHECK(window->GetPosition() == pos); + return window; +}; + +constexpr auto checkWithSize = [](auto size, auto* window) { + CHECK(window->GetSize() == size); + return window; +}; + +constexpr auto checkAll = [](auto* window, auto style, auto pos, auto size) { + return checkWithStyle(style, checkWithPosition(pos, checkWithSize(size, window))); +}; + +// How do we test chaining? +template +auto DoChainingIterations() +{ + auto style = WHICH::testStyle(); + auto pos = WHICH::testPosition(); + auto size = WHICH::testSize(); + auto expectedStyle = WHICH::expectedStyle(); + auto expectedPos = WHICH::expectedPosition(); + auto expectedSize = WHICH::expectedSize(); + wxFrame frame { nullptr, wxID_ANY, "" }; + { + auto uut = addWithStyle(style, addWithPosition(pos, addWithSize(size, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } + { + auto uut = addWithPosition(pos, addWithStyle(style, addWithSize(size, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } + { + auto uut = addWithSize(size, addWithStyle(style, addWithPosition(pos, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } + { + auto uut = addWithStyle(style, addWithSize(size, addWithPosition(pos, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } + { + auto uut = addWithPosition(pos, addWithSize(size, addWithStyle(style, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } + { + auto uut = addWithSize(size, addWithPosition(pos, addWithStyle(style, WHICH::createUUT()))); + checkAll(dynamic_cast(uut.create(&frame)), expectedStyle, expectedPos, expectedSize); + } +} + +#define CHAINING_TEST(WHICH) \ + SECTION("check all iterations") \ + { \ + DoChainingIterations(); \ + } + +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) diff --git a/tests/wxUI_TextCtrlTests.cpp b/tests/wxUI_TextCtrlTests.cpp index 238e681..ba85d87 100644 --- a/tests/wxUI_TextCtrlTests.cpp +++ b/tests/wxUI_TextCtrlTests.cpp @@ -21,26 +21,40 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::TextCtrl; + +struct TextCtrlTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxTE_PROCESS_ENTER; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return TextCtrlTestPolicy::createUUT(); } + TEST_CASE("TextCtrl") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -108,7 +122,7 @@ TEST_CASE("TextCtrl") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxTE_PROCESS_ENTER); + auto uut = createUUT().withStyle(wxTE_PROCESS_ENTER); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == (wxBORDER_SUNKEN | wxTE_PROCESS_ENTER)); } @@ -116,7 +130,7 @@ TEST_CASE("TextCtrl") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -124,9 +138,11 @@ TEST_CASE("TextCtrl") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + CHAINING_TEST(TextCtrlTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) diff --git a/tests/wxUI_TextTests.cpp b/tests/wxUI_TextTests.cpp index 12084ae..7283ca0 100644 --- a/tests/wxUI_TextTests.cpp +++ b/tests/wxUI_TextTests.cpp @@ -21,26 +21,40 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "wxUI_TestControlCommon.h" #include #include #include -// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while) using TypeUnderTest = wxUI::Text; + +struct TextTestPolicy { + using TypeUnderTest = TypeUnderTest; + static auto createUUT() { return TypeUnderTest {}; } + static auto testStyle() { return wxST_ELLIPSIZE_MIDDLE; } + static auto testPosition() { return wxPoint { 1, 2 }; } + static auto testSize() { return wxSize { 10, 12 }; } + static auto expectedStyle() { return testStyle(); } + static auto expectedPosition() { return testPosition(); } + static auto expectedSize() { return testSize(); } +}; +static auto createUUT() { return TextTestPolicy::createUUT(); } + TEST_CASE("Text") { SECTION("noargs.1") { wxFrame frame { nullptr, wxID_ANY, "" }; - TypeUnderTest uut {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } SECTION("noargs") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}; + auto uut = createUUT(); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetLabel().empty()); } @@ -108,7 +122,7 @@ TEST_CASE("Text") SECTION("style") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withStyle(wxST_ELLIPSIZE_MIDDLE); + auto uut = createUUT().withStyle(wxST_ELLIPSIZE_MIDDLE); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetWindowStyle() == wxST_ELLIPSIZE_MIDDLE); } @@ -116,7 +130,7 @@ TEST_CASE("Text") SECTION("pos") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withPosition({ 1, 2 }); + auto uut = createUUT().withPosition({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetPosition() == wxPoint { 1, 2 }); } @@ -124,9 +138,29 @@ TEST_CASE("Text") SECTION("size") { wxFrame frame { nullptr, wxID_ANY, "" }; - auto uut = TypeUnderTest {}.withSize({ 1, 2 }); + auto uut = createUUT().withSize({ 1, 2 }); auto* window = dynamic_cast(uut.create(&frame)); CHECK(window->GetSize() == wxSize { 1, 2 }); } + + SECTION("proxy.bind") + { + wxFrame frame { nullptr, wxID_ANY, "" }; + auto proxy = TypeUnderTest::Proxy {}; + auto uut = proxy.bind(TypeUnderTest { "label1" }.withSize({ 1, 2 })); + uut.create(&frame); + CHECK(proxy->get() == "label1"); + // CHECK(static_cast(proxy) == "label1"); + // CHECK("label1" == static_cast(proxy)); + // CHECK(static_cast(proxy) == "label1"); + proxy->set("label2"); + CHECK(proxy->get() == "label2"); + *proxy = "label3"; + CHECK(proxy->get() == "label3"); + std::string result2 = *proxy; + CHECK(result2 == "label3"); + } + + CHAINING_TEST(TextTestPolicy) } -// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity) +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers, readability-function-cognitive-complexity, misc-use-anonymous-namespace, cppcoreguidelines-avoid-do-while)