diff --git a/src/Wt/WBootstrap2Theme.C b/src/Wt/WBootstrap2Theme.C index 3c9c6411..226b6649 100644 --- a/src/Wt/WBootstrap2Theme.C +++ b/src/Wt/WBootstrap2Theme.C @@ -10,6 +10,7 @@ #include "Wt/WAbstractSpinBox.h" #include "Wt/WApplication.h" #include "Wt/WCheckBox.h" +#include "Wt/WCompositeWidget.h" #include "Wt/WDateEdit.h" #include "Wt/WDatePicker.h" #include "Wt/WDialog.h" @@ -204,6 +205,8 @@ void WBootstrap2Theme::apply(WWidget *widget, if (!widget->isThemeStyleEnabled()) return; + Wt::WCompositeWidget *composite = dynamic_cast(widget); + { WPopupWidget *popup = dynamic_cast(widget); if (popup) { @@ -265,19 +268,19 @@ void WBootstrap2Theme::apply(WWidget *widget, case DomElementType::DIV: { WDialog *dialog = dynamic_cast(widget); - if (dialog) { + if (dialog || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "modal"); return; } WPanel *panel = dynamic_cast(widget); - if (panel) { + if (panel || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "accordion-group"); return; } WProgressBar *bar = dynamic_cast(widget); - if (bar) { + if (bar || (composite && composite->canCastTo())) { switch (elementRole) { case MainElement: element.addPropertyWord(Property::Class, "progress"); @@ -293,19 +296,19 @@ void WBootstrap2Theme::apply(WWidget *widget, } WGoogleMap *map = dynamic_cast(widget); - if (map) { + if (map || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-googlemap"); return; } WAbstractItemView *itemView = dynamic_cast(widget); - if (itemView) { + if (itemView || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "form-inline"); return; } WNavigationBar *navBar = dynamic_cast(widget); - if (navBar) { + if (navBar || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "navbar"); return; } @@ -356,19 +359,19 @@ void WBootstrap2Theme::apply(WWidget *widget, case DomElementType::INPUT: { WAbstractSpinBox *spinBox = dynamic_cast(widget); - if (spinBox) { + if (spinBox || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-spinbox"); return; } WDateEdit *dateEdit = dynamic_cast(widget); - if (dateEdit) { + if (dateEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-dateedit"); return; } WTimeEdit *timeEdit = dynamic_cast(widget); - if (timeEdit) { + if (timeEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-timeedit"); return; } @@ -410,12 +413,12 @@ void WBootstrap2Theme::apply(WWidget *widget, { WInPlaceEdit *inPlaceEdit = dynamic_cast(widget); - if (inPlaceEdit) + if (inPlaceEdit || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-in-place-edit"); else { WDatePicker *picker = dynamic_cast(widget); - if (picker) + if (picker || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-datepicker"); } diff --git a/src/Wt/WBootstrap3Theme.C b/src/Wt/WBootstrap3Theme.C index dc3d6228..9614ec66 100644 --- a/src/Wt/WBootstrap3Theme.C +++ b/src/Wt/WBootstrap3Theme.C @@ -12,6 +12,7 @@ #include "Wt/WBootstrapTheme.h" #include "Wt/WCheckBox.h" #include "Wt/WComboBox.h" +#include "Wt/WCompositeWidget.h" #include "Wt/WDateEdit.h" #include "Wt/WDatePicker.h" #include "Wt/WLinkedCssStyleSheet.h" @@ -236,6 +237,8 @@ void WBootstrap3Theme::apply(WWidget *widget, DomElement& element, if (!widget->isThemeStyleEnabled()) return; + Wt::WCompositeWidget *composite = dynamic_cast(widget); + { WPopupWidget *popup = dynamic_cast(widget); if (popup) { @@ -297,19 +300,19 @@ void WBootstrap3Theme::apply(WWidget *widget, DomElement& element, case DomElementType::DIV: { WDialog *dialog = dynamic_cast(widget); - if (dialog) { + if (dialog || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "modal-dialog Wt-dialog"); return; } WPanel *panel = dynamic_cast(widget); - if (panel) { + if (panel || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "panel panel-default"); return; } WProgressBar *bar = dynamic_cast(widget); - if (bar) { + if (bar || (composite && composite->canCastTo())) { switch (elementRole) { case MainElement: element.addPropertyWord(Property::Class, "progress"); @@ -325,19 +328,19 @@ void WBootstrap3Theme::apply(WWidget *widget, DomElement& element, } WGoogleMap *map = dynamic_cast(widget); - if (map) { + if (map || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-googlemap"); return; } WAbstractItemView *itemView = dynamic_cast(widget); - if (itemView) { + if (itemView || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "form-inline"); return; } WNavigationBar *navBar = dynamic_cast(widget); - if (navBar) { + if (navBar || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "navbar navbar-default"); return; } @@ -394,19 +397,19 @@ void WBootstrap3Theme::apply(WWidget *widget, DomElement& element, } WAbstractSpinBox *spinBox = dynamic_cast(widget); - if (spinBox) { + if (spinBox || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-spinbox"); return; } WDateEdit *dateEdit = dynamic_cast(widget); - if (dateEdit) { + if (dateEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-dateedit"); return; } WTimeEdit *timeEdit = dynamic_cast(widget); - if (timeEdit) { + if (timeEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-timeedit"); return; } @@ -454,12 +457,12 @@ void WBootstrap3Theme::apply(WWidget *widget, DomElement& element, { WInPlaceEdit *inPlaceEdit = dynamic_cast(widget); - if (inPlaceEdit) + if (inPlaceEdit || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-in-place-edit"); else { WDatePicker *picker = dynamic_cast(widget); - if (picker) + if (picker || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-datepicker"); } } diff --git a/src/Wt/WBootstrap5Theme.C b/src/Wt/WBootstrap5Theme.C index 6ac05fa2..f390eff5 100644 --- a/src/Wt/WBootstrap5Theme.C +++ b/src/Wt/WBootstrap5Theme.C @@ -10,6 +10,7 @@ #include "Wt/WAbstractSpinBox.h" #include "Wt/WApplication.h" #include "Wt/WCheckBox.h" +#include "Wt/WCompositeWidget.h" #include "Wt/WDateEdit.h" #include "Wt/WDatePicker.h" #include "Wt/WLinkedCssStyleSheet.h" @@ -245,6 +246,9 @@ void WBootstrap5Theme::apply(WWidget *widget, DomElement& element, if (!widget->isThemeStyleEnabled()) return; + + Wt::WCompositeWidget *composite = dynamic_cast(widget); + { auto popup = dynamic_cast(widget); if (popup) { @@ -306,19 +310,19 @@ void WBootstrap5Theme::apply(WWidget *widget, DomElement& element, case DomElementType::DIV: { auto dialog = dynamic_cast(widget); - if (dialog) { + if (dialog || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "modal Wt-dialog"); return; } auto panel = dynamic_cast(widget); - if (panel) { + if (panel || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "card Wt-panel"); return; } auto bar = dynamic_cast(widget); - if (bar) { + if (bar || (composite && composite->canCastTo())) { switch (elementRole) { case MainElement: element.addPropertyWord(Property::Class, "progress"); @@ -331,7 +335,7 @@ void WBootstrap5Theme::apply(WWidget *widget, DomElement& element, } auto map = dynamic_cast(widget); - if (map) { + if (map || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-googlemap"); return; } @@ -415,19 +419,19 @@ void WBootstrap5Theme::apply(WWidget *widget, DomElement& element, } auto spinBox = dynamic_cast(widget); - if (spinBox) { + if (spinBox || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-spinbox"); return; } auto dateEdit = dynamic_cast(widget); - if (dateEdit) { + if (dateEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-dateedit"); return; } auto timeEdit = dynamic_cast(widget); - if (timeEdit) { + if (timeEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-timeedit"); return; } @@ -481,11 +485,11 @@ void WBootstrap5Theme::apply(WWidget *widget, DomElement& element, element.addPropertyWord(Property::Class, "form-file-button"); auto inPlaceEdit = dynamic_cast(widget); - if (inPlaceEdit) + if (inPlaceEdit || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-in-place-edit"); else { auto picker = dynamic_cast(widget); - if (picker) + if (picker || (composite && composite->canCastTo())) element.addPropertyWord(Property::Class, "Wt-datepicker"); } } diff --git a/src/Wt/WCompositeWidget.h b/src/Wt/WCompositeWidget.h index bb3367b9..1da8000e 100644 --- a/src/Wt/WCompositeWidget.h +++ b/src/Wt/WCompositeWidget.h @@ -9,6 +9,11 @@ #include +// Conditionally include ONLY IF the compiler supports C++20 Concepts +#ifdef __cpp_concepts +#include +#endif + namespace Wt { /*! \class WCompositeWidget Wt/WCompositeWidget.h Wt/WCompositeWidget.h @@ -63,6 +68,36 @@ public: ~WCompositeWidget(); + /** + * @brief helper Function for Theme::apply to check if underlying impl can be cast to predefined Widgets + * \warning Not Every Widget is Supported! + */ + template + #ifdef __cpp_concepts + // Constraint 1: T must be a pointer type + // Constraint 2: The type T points to (std::remove_pointer_t) must be WWidget or derived from it. + requires std::is_pointer_v && (std::derived_from, WWidget> || std::same_as, WWidget>) + #endif + bool canCastTo() const { + // Fallback static_assert for older compilers + #ifndef __cpp_concepts + // Ensure T is a pointer type + static_assert(std::is_pointer::value, + "Template argument T must be a pointer type (e.g., WPanel*)."); + + // Ensure the pointed-to type is derived from WWidget + static_assert(std::is_base_of>::value, + "The type pointed to by T must be a WWidget or derived from WWidget."); + #endif + + if (!impl_) { + return false; // Cannot cast if no implementation widget exists + } + + // Perform the dynamic_cast and check if it succeeds + return dynamic_cast(impl_.get()) != nullptr; + } + virtual std::vector children() const override; using WWidget::removeWidget; virtual std::unique_ptr removeWidget(WWidget *widget) override; diff --git a/src/Wt/WCssTheme.C b/src/Wt/WCssTheme.C index e1db08fb..366eebba 100644 --- a/src/Wt/WCssTheme.C +++ b/src/Wt/WCssTheme.C @@ -11,6 +11,7 @@ #include "Wt/WApplication.h" #include "Wt/WCalendar.h" #include "Wt/WCssTheme.h" +#include "Wt/WCompositeWidget.h" #include "Wt/WDateEdit.h" #include "Wt/WDialog.h" #include "Wt/WEnvironment.h" @@ -162,6 +163,8 @@ void WCssTheme::apply(WWidget *widget, DomElement& element, int elementRole) element.addPropertyWord(Property::Class, "Wt-outset"); } + Wt::WCompositeWidget *composite = dynamic_cast(widget); + switch (element.type()) { case DomElementType::BUTTON: if (creating) { @@ -213,19 +216,19 @@ void WCssTheme::apply(WWidget *widget, DomElement& element, int elementRole) case DomElementType::DIV: { WDialog *dialog = dynamic_cast(widget); - if (dialog) { + if (dialog || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-dialog"); return; } WPanel *panel = dynamic_cast(widget); - if (panel) { + if (panel || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-panel Wt-outset"); return; } WProgressBar *bar = dynamic_cast(widget); - if (bar) { + if (bar || (composite && composite->canCastTo())) { switch (elementRole) { case MainElement: element.addPropertyWord(Property::Class, "Wt-progressbar"); @@ -245,19 +248,19 @@ void WCssTheme::apply(WWidget *widget, DomElement& element, int elementRole) case DomElementType::INPUT: { WAbstractSpinBox *spinBox = dynamic_cast(widget); - if (spinBox) { + if (spinBox || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-spinbox"); return; } WDateEdit *dateEdit = dynamic_cast(widget); - if (dateEdit) { + if (dateEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-dateedit"); return; } WTimeEdit *timeEdit = dynamic_cast(widget); - if (timeEdit) { + if (timeEdit || (composite && composite->canCastTo())) { element.addPropertyWord(Property::Class, "Wt-timeedit"); return; }