#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 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; } inline void setAccessKey(WInteractWidget& widget, const char mnemonic) { widget.setAttributeValue("accesskey", string(1, tolower(mnemonic))); } class CApplication final : public CThemedApplication { public: CApplication(const WEnvironment& rcEnv); void suppressBrowserContextMenu() { setHtmlAttribute("oncontextmenu", "event.cancelBubble = true; event.returnValue = false; return false;"); } private: void triggerDialogSizeChange() { #if 0 _Server.post(sessionId(), [this] { _Dialog.setMinimumSize(200, 150); triggerUpdate(); }); #endif } WServer& _Server; //!< The associated WServer instance, needed for async operations. WDialog _Dialog; WContainerWidget& _cntRoot{*root()}; WBoxLayout& _lytRoot{*_cntRoot.setLayout(make_unique())}; unique_ptr _mnuSub{make_unique()}; WMenuItem& _miSub{*_mnuSub->addItem("subitem")}; WPushButton& _btnP{*_lytRoot.addWidget(create([this](auto& btnP) { btnP.clicked().connect([this] { _Dialog.show(); triggerDialogSizeChange(); }); btnP.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnP, 'p'); }, "Show popup"))}; WCheckBox& _chkVertical{*_lytRoot.addWidget(make_unique("popup orientation 'Vertical'"))}; WTabWidget& _tbwMain{*_lytRoot.addWidget(make_unique(), /*stretch*/ 100)}; WMenuItem& _tabConnecting{*_tbwMain.addTab(make_unique(), "connecting...", ContentLoading::Eager)}; WMenuItem& _tabA{*_tbwMain.addTab(make_unique(), "A", ContentLoading::Eager)}; WContainerWidget& _cntA{dynamic_cast(*_tbwMain.widget(_tbwMain.count() - 1))}; WBoxLayout& _lytA{*_cntA.setLayout(make_unique())}; WTableView& _tvNarrow{*_lytA.addWidget(make_unique())}; WTableView& _tvA{*_lytA.addWidget(make_unique())}; 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& _btnA{*_lytBottom.addWidget(create([this](auto& btnA) { btnA.clicked().connect([this] { WLineEdit& ed{*_Dialog.contents()->addNew()}; ed.keyWentDown().connect([this, &ed](const WKeyEvent& e) { log("trace") << "ed.keyWentDown " << (int)e.key() << ' ' << (ed.hasFocus() ? "focused" : ""); }); ed.escapePressed().connect([this] { _Dialog.reject(); }); if (ed.isVisible()) ed.setFocus(); }); btnA.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnA, 'A'); }, "Add line edit to dialog"))}; WPushButton& _btnB{*_lytBottom.addWidget(create([this](auto& btnB) { btnB.clicked().connect([this] { _Dialog.setMinimumSize(300, 200); }); btnB.setTextFormat(TextFormat::UnsafeXHTML); setAccessKey(btnB, 's'); }, "setMinimumSize of dialog"))}; shared_ptr _mdlNarrowPtr{make_shared(100, 2)}; shared_ptr _mdlAPtr {make_shared( 20, 50)}; shared_ptr _mdlBPtr {make_shared(200, 100)}; const vector> _appWidgets{_tabA, _tabB, _pnlBottom}; Orientation _orientation{Orientation::Horizontal}; }; CApplication::CApplication(const WEnvironment& rcEnv) : CThemedApplication{rcEnv} , _Server{*environment().server()} { setTitle("bug repro " WT_VERSION_STR); _Dialog.setWindowTitle("non-modal popup"); _Dialog.setModal(false); _Dialog.setClosable(true); _Dialog.setResizable(true); _Dialog.escapePressed().connect([this] { _Dialog.reject(); }); _Dialog.shown().connect([this] { _Dialog.setFirstFocus(); }); _Dialog.contents()->addNew("info"); _Dialog.keyWentDown().connect([this](const WKeyEvent& e) { log("trace") << "dlg.keyWentDown " << (int)e.key() << ' ' << (_Dialog.hasFocus() ? "focused" : ""); }); globalKeyWentDown().connect([this](const WKeyEvent& e) { log("trace") << "globalKeyWentDown " << (int)e.key(); }); _chkVertical.changed().connect([this] { _orientation = _chkVertical.isChecked() ? Orientation::Vertical : Orientation::Horizontal; log("trace") << _chkVertical.isChecked(); }); _chkVertical.setChecked(); _chkVertical.changed().emit(); _btnA.clicked().emit({}); suppressBrowserContextMenu(); const auto initTable = [this](WTableView& tv, const shared_ptr& mdlPtr) { tv.keyWentDown().connect([this, &tv](const WKeyEvent& e) { log("trace") << "tv.keyWentDown " << (int)e.key() << ' ' << (tv.hasFocus() ? "focused" : ""); }); tv.setRowHeaderCount(1); tv.setSelectionBehavior(SelectionBehavior::Items); tv.setSelectionMode(SelectionMode::Single); tv.setSortingEnabled(false); tv.setCanReceiveFocus(true); tv.setEditTriggers(EditTrigger::None); tv.setAlternatingRowColors(true); tv.setModel(mdlPtr); tv.selectionChanged().connect([this, &tv] { const WModelIndexSet selectedIndexes{tv.selectionModel()->selectedIndexes()}; switch (selectedIndexes.size()) { case 0: break; case 1: tv.scrollTo(*selectedIndexes.cbegin()); break; default: throw logic_error("multi-selection unexpected"); } }); tv.select(mdlPtr->index(0, 1)); tv.mouseWentDown().connect([&tv, this](const WModelIndex& index, const WMouseEvent& e) { if (e.button() != MouseButton::Right) return; tv.select(index); if (e.modifiers() == KeyboardModifier::Control) { _Dialog.show(); } _Server.post(sessionId(), [this, e] { _Dialog.positionAt(e); triggerDialogSizeChange(); }); }); tv.keyWentDown().connect([this, &tv](const WKeyEvent& e) { if (e.key() != Key::Space) return; _Dialog.positionAt(tv.itemWidget(*tv.selectionModel()->selectedIndexes().cbegin()), _orientation); triggerDialogSizeChange(); }); }; initTable(_tvNarrow, _mdlNarrowPtr); initTable(_tvA, _mdlAPtr); initTable(_tvB, _mdlBPtr); _lytBottom.setContentsMargins(0, 0, 0, 0); _lytBottom.addStretch(100); _lytA.addStretch(100); const auto showAppWidgets{[this](const bool show) { for (WWidget& appWidget : _appWidgets) { appWidget.setHidden(!show); } }}; showAppWidgets(false); _Server.schedule(300ms, sessionId(), [this, showAppWidgets = move(showAppWidgets)] { showAppWidgets(true); _tabConnecting.hide(); log("trace") << "canReceiveFocus " << _tvA.canReceiveFocus(); // is true (by default) _tabA.setFirstFocus(); // Why doesn't this have any effect?!? _tvA.setFocus(); triggerUpdate(); }); // 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; } }); }