From 66599a9896e0cf69b58c0a73152aba4750d87af2 Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Mon, 11 Mar 2024 21:10:53 +0100 Subject: Implemented ability to open external links in news and chat * Use ConfirmDialog to confirm the opening of the external link. * ConfirmDialog now centers on its parent window when provided. * Reset hovered link when mouse exits the BrowserBox. --- src/gui/chatwindow.cpp | 2 +- src/gui/updaterwindow.cpp | 5 ++++- src/gui/updaterwindow.h | 5 +++-- src/gui/widgets/browserbox.cpp | 11 +++++++---- src/gui/widgets/browserbox.h | 1 + src/gui/widgets/itemlinkhandler.cpp | 34 ++++++++++++++++++++++++++++------ src/gui/widgets/itemlinkhandler.h | 20 +++++++++++++++++--- src/gui/widgets/window.cpp | 5 ++++- src/utils/stringutils.h | 8 ++++++++ 9 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/gui/chatwindow.cpp b/src/gui/chatwindow.cpp index d51c3a25..041e08e9 100644 --- a/src/gui/chatwindow.cpp +++ b/src/gui/chatwindow.cpp @@ -106,7 +106,7 @@ ChatWindow::ChatWindow(): setMinWidth(150); setMinHeight(90); - mItemLinkHandler = new ItemLinkHandler; + mItemLinkHandler = new ItemLinkHandler(this); mChatInput = new ChatInput; mChatInput->setActionEventId("chatinput"); diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp index 8c63ef6a..c8ed66e1 100644 --- a/src/gui/updaterwindow.cpp +++ b/src/gui/updaterwindow.cpp @@ -33,6 +33,7 @@ #include "gui/widgets/layout.h" #include "gui/widgets/progressbar.h" #include "gui/widgets/scrollarea.h" +#include "gui/widgets/itemlinkhandler.h" #include "net/download.h" @@ -123,7 +124,8 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mUpdateHost(updateHost), mUpdatesDir(updatesDir), mCurrentFile("news.txt"), - mLoadUpdates(applyUpdates) + mLoadUpdates(applyUpdates), + mLinkHandler(std::make_unique(this)) { setWindowName("UpdaterWindow"); setResizable(true); @@ -138,6 +140,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mCancelButton = new Button(_("Cancel"), "cancel", this); mPlayButton = new Button(_("Play"), "play", this); + mBrowserBox->setLinkHandler(mLinkHandler.get()); mProgressBar->setSmoothProgress(false); mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mPlayButton->setEnabled(false); diff --git a/src/gui/updaterwindow.h b/src/gui/updaterwindow.h index 2d1b059b..6ea1d754 100644 --- a/src/gui/updaterwindow.h +++ b/src/gui/updaterwindow.h @@ -31,9 +31,11 @@ #include #include +#include #include #include +class LinkHandler; class BrowserBox; class Button; class ProgressBar; @@ -99,8 +101,6 @@ class UpdaterWindow : public Window, public gcn::ActionListener, void logic() override; - int updateState; - private: void download(); @@ -189,6 +189,7 @@ private: ProgressBar *mProgressBar; /**< Update progress bar. */ BrowserBox *mBrowserBox; /**< Box to display news. */ ScrollArea *mScrollArea; /**< Used to scroll news box. */ + std::unique_ptr mLinkHandler; }; #endif diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp index d12562f1..8814a995 100644 --- a/src/gui/widgets/browserbox.cpp +++ b/src/gui/widgets/browserbox.cpp @@ -70,9 +70,7 @@ BrowserBox::BrowserBox(unsigned int mode): addMouseListener(this); } -BrowserBox::~BrowserBox() -{ -} +BrowserBox::~BrowserBox() = default; void BrowserBox::addRow(const std::string &row) { @@ -177,6 +175,11 @@ void BrowserBox::mouseMoved(gcn::MouseEvent &event) updateHoveredLink(event.getX(), event.getY()); } +void BrowserBox::mouseExited(gcn::MouseEvent &event) +{ + mHoveredLink.reset(); +} + void BrowserBox::draw(gcn::Graphics *graphics) { const gcn::ClipRectangle &cr = graphics->getCurrentClipArea(); @@ -285,7 +288,7 @@ void BrowserBox::layoutTextRow(TextRow &row, LayoutContext &context) int x = 0; // Check for separator lines - if (row.text.find("---", 0) == 0) + if (startsWith(row.text, "---")) { for (x = 0; x < getWidth(); x += context.minusWidth - 1) { diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h index 11b391e0..a87634fd 100644 --- a/src/gui/widgets/browserbox.h +++ b/src/gui/widgets/browserbox.h @@ -118,6 +118,7 @@ class BrowserBox : public gcn::Widget, */ void mousePressed(gcn::MouseEvent &event) override; void mouseMoved(gcn::MouseEvent &event) override; + void mouseExited(gcn::MouseEvent &event) override; /** * Draws the browser box. diff --git a/src/gui/widgets/itemlinkhandler.cpp b/src/gui/widgets/itemlinkhandler.cpp index 81fa2ba7..e15aa6d0 100644 --- a/src/gui/widgets/itemlinkhandler.cpp +++ b/src/gui/widgets/itemlinkhandler.cpp @@ -22,6 +22,7 @@ #include #include +#include "gui/confirmdialog.h" #include "gui/itempopup.h" #include "gui/viewport.h" @@ -29,22 +30,37 @@ #include "resources/iteminfo.h" #include "resources/itemdb.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" -ItemLinkHandler::ItemLinkHandler() +ItemLinkHandler::ItemLinkHandler(Window *parent) + : mParent(parent) { - mItemPopup = new ItemPopup; + mItemPopup = std::make_unique(); } -ItemLinkHandler::~ItemLinkHandler() +ItemLinkHandler::~ItemLinkHandler() = default; + +static bool isUrl(const std::string &link) { - delete mItemPopup; + return startsWith(link, "https://") || + startsWith(link, "http://") || + startsWith(link, "ftp://"); } void ItemLinkHandler::handleLink(const std::string &link) { + if (isUrl(link)) + { + mLink = link; + + mConfirmDialog = new ConfirmDialog(_("Open URL?"), link, mParent); + mConfirmDialog->addActionListener(this); + return; + } + int id = 0; - std::stringstream stream; - stream << link; + std::istringstream stream(link); stream >> id; if (id > 0) @@ -58,3 +74,9 @@ void ItemLinkHandler::handleLink(const std::string &link) mItemPopup->position(viewport->getMouseX(), viewport->getMouseY()); } } + +void ItemLinkHandler::action(const gcn::ActionEvent &actionEvent) +{ + if (actionEvent.getId() == "yes") + SDL_OpenURL(mLink.c_str()); +} diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h index d0f00b25..3bb0b557 100644 --- a/src/gui/widgets/itemlinkhandler.h +++ b/src/gui/widgets/itemlinkhandler.h @@ -24,18 +24,32 @@ #include "gui/widgets/linkhandler.h" +#include + +#include + +class ConfirmDialog; class ItemPopup; +class Window; -class ItemLinkHandler : public LinkHandler +class ItemLinkHandler : public LinkHandler, gcn::ActionListener { public: - ItemLinkHandler(); + ItemLinkHandler(Window *parent = nullptr); ~ItemLinkHandler() override; + // LinkHandler interface void handleLink(const std::string &link) override; + // ActionListener interface + void action(const gcn::ActionEvent &actionEvent) override; + private: - ItemPopup *mItemPopup; + std::unique_ptr mItemPopup; + ConfirmDialog *mConfirmDialog = nullptr; + + Window *mParent = nullptr; + std::string mLink; }; #endif diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index 6290ef62..e09986d2 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -765,7 +765,10 @@ void Window::redraw() void Window::center() { - setLocationRelativeTo(getParent()); + if (auto window = getParentWindow()) + setLocationRelativeTo(window); + else + setLocationRelativeTo(getParent()); } void Window::ensureOnScreen() diff --git a/src/utils/stringutils.h b/src/utils/stringutils.h index cd80b60f..8e5f1102 100644 --- a/src/utils/stringutils.h +++ b/src/utils/stringutils.h @@ -120,6 +120,14 @@ std::string removeColors(std::string msg); */ int compareStrI(const std::string &a, const std::string &b); +/** + * Returns whether a string starts with a given prefix. + */ +inline bool startsWith(const std::string &str, const char *prefix) +{ + return str.rfind(prefix, 0) == 0; +} + /** * Tells wether the character is a word separator. */ -- cgit v1.2.3-60-g2f50