#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #pragma GCC diagnostic pop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Wt; using namespace Wt::cpp17; #define SET_BS5THEME 1 // Always apply theme before creating widgets! class CThemedApplication : public WApplication { public: CThemedApplication(const WEnvironment& rcEnv) : WApplication{rcEnv} { #if SET_BS5THEME setTheme(make_shared()); #endif } }; class CFixedTableModel : public WAbstractTableModel { public: CFixedTableModel(const int rowCount, const int colCount) : _rowCount{rowCount} , _colCount{colCount} { } any headerData(const int section, const Orientation orientation, const ItemDataRole role) const { if (orientation != Orientation::Horizontal) throw invalid_argument("orientation"); switch (role.value()) { default: return {}; case ItemDataRole::Display: return format("C{}", section); } } any data(const WModelIndex& index, const ItemDataRole role) const override { if (!index.isValid()) return {}; switch (role.value()) { default: return {}; case ItemDataRole::Display: return format("{}, {}", index.row(), index.column()); } } int columnCount(const WModelIndex& index) const override { return index.isValid() ? 0 : _colCount; } int rowCount (const WModelIndex& index) const override { return index.isValid() ? 0 : _rowCount; } const int _rowCount; const int _colCount; }; template constexpr inline std::unique_ptr create(std::function&& fInitialize, TArgs&&... args) { std::unique_ptr Ptr{std::make_unique(std::forward(args)...)}; fInitialize(*Ptr); return Ptr; } template <> class std::formatter> : public formatter { public: auto format(const WFlags& rcModifiers, format_context& rCtx) const { return formatter::format(std::format("0x{:02x}", rcModifiers.value()), rCtx); } }; template <> class std::formatter : public formatter { public: auto format(const WKeyEvent& rcKeyEvent, format_context& rCtx) const { return formatter::format(std::format("{{ key: '{}' {} 0x{:02x}, modifiers: {}, charCode: '{}' {} 0x{:02x} }}" , static_cast(rcKeyEvent.key()), static_cast(rcKeyEvent.key()), static_cast(rcKeyEvent.key()) , rcKeyEvent.modifiers() , static_cast(rcKeyEvent.charCode()), rcKeyEvent.charCode(), rcKeyEvent.charCode() ) , rCtx ); } }; class CApplication final : public CThemedApplication { public: CApplication(const WEnvironment& rcEnv); static void setAccessKey(WInteractWidget& widget, const char mnemonic) { widget.setAttributeValue("accesskey", string(1, tolower(mnemonic))); } static void suppressBrowserContextMenu(WWidget& rWidget) { rWidget.setAttributeValue("oncontextmenu", "event.cancelBubble = true; event.returnValue = false; return false;"); } private: WServer& _Server; //!< The associated WServer instance, needed for async operations. WContainerWidget& _cntRoot{*root()}; WBoxLayout& _lytRoot{*_cntRoot.setLayout(make_unique())}; unique_ptr _mnuContext{make_unique()}; WMenuItem& _miContext{*_mnuContext->addItem("context")}; unique_ptr _mnuSub{make_unique()}; WMenuItem& _miSubLong{*_mnuSub->addItem("subitem")}; unique_ptr _mnuMain{make_unique()}; WMenuItem& _miMainItem {[this] -> WMenuItem& { WMenuItem& item{*_mnuMain->addItem("popup")}; item.clicked().connect([this] { _dlgPopup.show(); }); return item; }()}; WMenuItem& _miMainSubmenu{[this] -> WMenuItem& { WMenuItem& item{*_mnuMain->addItem("submenu")}; item.setMenu(move(_mnuSub)); return item; }()}; WMenu& _mainMenu{*_lytRoot.addWidget(make_unique())}; WMenuItem& _miMain {[this] -> WMenuItem& { WMenuItem& item{*_mainMenu.addItem("main")}; item.setMenu(move(_mnuMain)); return item; }()}; WLineEdit& _ed{*_lytRoot.addWidget(make_unique())}; WPushButton& _btnPopup{*_lytRoot.addWidget(create([this](auto& btnPopup) { btnPopup.clicked().connect([this] { log("trace") << "_btnPopup.clicked"; _dlgPopup.show(); }); btnPopup.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnPopup, 'P'); }, "Popup"))}; WTabWidget& _tbwMain{*_lytRoot.addWidget(make_unique(), /*stretch*/ 100)}; WMenuItem& _tabA{*_tbwMain.addTab(make_unique(), "A", ContentLoading::Eager)}; WTableView& _tvA{dynamic_cast(*_tbwMain.widget(_tbwMain.count() - 1))}; WMenuItem& _tabB{*_tbwMain.addTab(make_unique(), "B", ContentLoading::Eager)}; WTableView& _tvB{dynamic_cast(*_tbwMain.widget(_tbwMain.count() - 1))}; WPanel& _pnlBottom{*_lytRoot.addWidget(make_unique())}; WContainerWidget& _cntBottom{*_pnlBottom.setCentralWidget(make_unique())}; WBoxLayout& _lytBottom{*_cntBottom.setLayout(make_unique())}; WPushButton& _btnN{*_lytBottom.addWidget(create([this](auto& btnN) { btnN.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnN, 'N'); }, "N"))}; shared_ptr _mdlAPtr{make_shared(100, 50)}; WDialog _dlgPopup{}; WDialog _dlgNested{}; }; CApplication::CApplication(const WEnvironment& rcEnv) : CThemedApplication{rcEnv} , _Server{*environment().server()} { setTitle("bug repro " WT_VERSION_STR); suppressBrowserContextMenu(_cntRoot); suppressBrowserContextMenu(*_mnuContext); globalKeyWentDown().connect([this](const WKeyEvent& e) { log("trace") << format("globalKeyWentDown({})", e); }); globalKeyWentUp().connect([this](const WKeyEvent& e) { log("trace") << format("globalKeyWentUp({})", e); }); _lytBottom.setContentsMargins(0, 0, 0, 0); _lytBottom.addStretch(100); _Server.schedule(300ms, sessionId(), [this] { _dlgPopup.show(); triggerUpdate(); }); #if 0 _Server.schedule(3s, sessionId(), [this] { _dlgNested.show(); triggerUpdate(); }); #endif suppressBrowserContextMenu(_dlgNested); _dlgNested.setClosable(false); _dlgNested.setResizable(true); _dlgNested.setWindowTitle("Nested Popup"); _dlgNested.keyWentDown().connect([this](const WKeyEvent& e) { log("trace") << format("_dlgNested.keyWentDown({})", e); switch (e.key()) { default: return; case Key::Enter: case Key::Escape: _dlgNested.reject(); } }); _dlgNested.footer()->hide(); suppressBrowserContextMenu(_dlgPopup); _dlgPopup.setClosable(true); _dlgPopup.setWindowTitle("Popup (long title for wider dlg easily)"); _dlgPopup.keyWentDown().connect([this](const WKeyEvent& e) { log("trace") << format("_dlgPopup.keyWentDown({})", e); switch (e.key()) { default: return; case Key::Enter: case Key::Escape: _dlgPopup.reject(); } }); WVBoxLayout& lytPopup{*_dlgPopup.contents()->setLayout(make_unique())}; _dlgPopup.shown().connect([&lytPopup] { lytPopup.itemAt(1)->widget()->setFocus(); }); lytPopup.addWidget(create([this](auto& btnPopupNested) { btnPopupNested.clicked().connect([this] { log("trace") << "btnPopupNested.clicked"; _dlgNested.show(); }); btnPopupNested.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnPopupNested, 'N'); }, "Popup nested")); lytPopup.addWidget(make_unique()); lytPopup.addWidget(make_unique())->setModel(_mdlAPtr); lytPopup.addWidget(make_unique())->setModel(_mdlAPtr); const auto fTryAddKeyHandlers = [](WDialog& dlg, TWidget* const cpWidget) { if (cpWidget == nullptr) { return false; } TWidget& rWidget{*cpWidget}; rWidget.keyWentDown().connect([&dlg] (const WKeyEvent& rcKeyEvent) { dlg.keyWentDown().emit(rcKeyEvent); }); rWidget.keyWentUp ().connect([&dlg] (const WKeyEvent& rcKeyEvent) { if (rcKeyEvent.key() == Key::Escape) { dlg.escapePressed().emit(); } else { dlg.keyWentUp ().emit(rcKeyEvent); } }); rWidget.keyPressed ().connect([&dlg, cpWidget](const WKeyEvent& rcKeyEvent) { switch (rcKeyEvent.key()) { default: break; case Key::Escape: dlg.escapePressed().emit(); break; case Key::Enter: { if (dynamic_cast(cpWidget) != nullptr) { // Let the WPushButton widget trigger the action } #if 0 else if (WRadioButton* const cpRadioButton{dynamic_cast(cpWidget)}; cpRadioButton != nullptr) { setChecked(*cpRadioButton); } #endif else { dlg.enterPressed().emit(); } } break; } // switch (rcKeyEvent.key()) }); return true; }; const auto fAddKeyHandlers = [&fTryAddKeyHandlers, this](WWidget* const cpWidget) { fTryAddKeyHandlers(_dlgPopup, dynamic_cast(cpWidget)) || fTryAddKeyHandlers(_dlgPopup, dynamic_cast(cpWidget)); }; lytPopup.iterateWidgets(fAddKeyHandlers); // final action after creating the UI enableUpdates(); } int main(int argc, char* argv[]) { return WRun(argc, argv, [](const WEnvironment& rcEnv) { try { return make_unique(rcEnv); } catch (const std::exception& rcException) { cerr << "exception " << typeid(remove_cvref::type).name() << ": " << rcException.what() << endl; #if DEBUG exit(-1); #endif throw; } }); }