mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-27 17:28:41 +00:00
Add button_url[] and hypertext element to allow mods to open web pages (#13825)
Fixes #12500
This commit is contained in:
parent
6c4a110679
commit
24cc33e704
17 changed files with 530 additions and 37 deletions
|
@ -13,6 +13,7 @@ set(gui_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/guiInventoryList.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiItemImage.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiOpenURL.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp
|
||||
|
|
|
@ -976,14 +976,18 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen
|
|||
void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
|
||||
const std::string &type)
|
||||
{
|
||||
int expected_parts = (type == "button_url" || type == "button_url_exit") ? 5 : 4;
|
||||
std::vector<std::string> parts;
|
||||
if (!precheckElement("button", element, 4, 4, parts))
|
||||
if (!precheckElement("button", element, expected_parts, expected_parts, parts))
|
||||
return;
|
||||
|
||||
std::vector<std::string> v_pos = split(parts[0],',');
|
||||
std::vector<std::string> v_geom = split(parts[1],',');
|
||||
std::string name = parts[2];
|
||||
std::string label = parts[3];
|
||||
std::string url;
|
||||
if (type == "button_url" || type == "button_url_exit")
|
||||
url = parts[4];
|
||||
|
||||
MY_CHECKPOS("button",0);
|
||||
MY_CHECKGEOM("button",1);
|
||||
|
@ -1018,8 +1022,10 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
|
|||
258 + m_fields.size()
|
||||
);
|
||||
spec.ftype = f_Button;
|
||||
if(type == "button_exit")
|
||||
if (type == "button_exit" || type == "button_url_exit")
|
||||
spec.is_exit = true;
|
||||
if (type == "button_url" || type == "button_url_exit")
|
||||
spec.url = url;
|
||||
|
||||
GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc,
|
||||
data->current_parent, spec.fid, spec.flabel.c_str());
|
||||
|
@ -2897,7 +2903,7 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == "button" || type == "button_exit") {
|
||||
if (type == "button" || type == "button_exit" || type == "button_url" || type == "button_url_exit") {
|
||||
parseButton(data, description, type);
|
||||
return;
|
||||
}
|
||||
|
@ -4968,6 +4974,17 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
m_sound_manager->playSound(0, SoundSpec(s.sound, 1.0f));
|
||||
|
||||
s.send = true;
|
||||
|
||||
if (!s.url.empty()) {
|
||||
if (m_client) {
|
||||
// in game
|
||||
g_gamecallback->showOpenURLDialog(s.url);
|
||||
} else {
|
||||
// main menu
|
||||
porting::open_url(s.url);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.is_exit) {
|
||||
if (m_allowclose) {
|
||||
acceptInput(quit_mode_accept);
|
||||
|
|
|
@ -137,6 +137,7 @@ class GUIFormSpecMenu : public GUIModalMenu
|
|||
std::string fname;
|
||||
std::wstring flabel;
|
||||
std::wstring fdefault;
|
||||
std::string url;
|
||||
s32 fid;
|
||||
bool send;
|
||||
FormspecFieldType ftype;
|
||||
|
|
|
@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "inventory.h"
|
||||
#include "util/string.h"
|
||||
#include "irrlicht_changes/CGUITTFont.h"
|
||||
#include "mainmenumanager.h"
|
||||
#include "porting.h"
|
||||
|
||||
using namespace irr::gui;
|
||||
|
||||
|
@ -1106,6 +1108,18 @@ bool GUIHyperText::OnEvent(const SEvent &event)
|
|||
newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
|
||||
auto url_it = tag->attrs.find("url");
|
||||
if (url_it != tag->attrs.end()) {
|
||||
if (g_gamecallback) {
|
||||
// in game
|
||||
g_gamecallback->showOpenURLDialog(url_it->second);
|
||||
} else {
|
||||
// main menu
|
||||
porting::open_url(url_it->second);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
196
src/gui/guiOpenURL.cpp
Normal file
196
src/gui/guiOpenURL.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
Part of Minetest
|
||||
Copyright (C) 2023-24 rubenwardy
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "guiOpenURL.h"
|
||||
#include "guiButton.h"
|
||||
#include "guiEditBoxWithScrollbar.h"
|
||||
#include <IGUIEditBox.h>
|
||||
#include <IGUIFont.h>
|
||||
#include "client/renderingengine.h"
|
||||
#include "porting.h"
|
||||
#include "gettext.h"
|
||||
#include "util/colorize.h"
|
||||
|
||||
namespace {
|
||||
constexpr int ID_url = 256;
|
||||
constexpr int ID_open = 259;
|
||||
constexpr int ID_cancel = 261;
|
||||
}
|
||||
|
||||
GUIOpenURLMenu::GUIOpenURLMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
ISimpleTextureSource *tsrc, const std::string &url
|
||||
):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_tsrc(tsrc),
|
||||
url(url)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GUIOpenURLMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
/*
|
||||
Remove stuff
|
||||
*/
|
||||
removeAllChildren();
|
||||
|
||||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
const float s = m_gui_scale;
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 580 * s / 2,
|
||||
screensize.Y / 2 - 250 * s / 2,
|
||||
screensize.X / 2 + 580 * s / 2,
|
||||
screensize.Y / 2 + 250 * s / 2
|
||||
);
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
v2s32 topleft_client(40 * s, 0);
|
||||
|
||||
/*
|
||||
Get URL text
|
||||
*/
|
||||
bool ok = true;
|
||||
std::string text;
|
||||
#ifdef USE_CURL
|
||||
try {
|
||||
text = colorize_url(url);
|
||||
} catch (const std::exception &e) {
|
||||
text = std::string(e.what()) + " (url = " + url + ")";
|
||||
ok = false;
|
||||
}
|
||||
#else
|
||||
text = url;
|
||||
#endif
|
||||
|
||||
/*
|
||||
Add stuff
|
||||
*/
|
||||
s32 ypos = 40 * s;
|
||||
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 500 * s, 20 * s);
|
||||
rect += topleft_client + v2s32(20 * s, ypos);
|
||||
|
||||
std::wstring title = ok
|
||||
? wstrgettext("Open URL?")
|
||||
: wstrgettext("Unable to open URL");
|
||||
gui::StaticText::add(Environment, title, rect,
|
||||
false, true, this, -1);
|
||||
}
|
||||
|
||||
ypos += 50 * s;
|
||||
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 440 * s, 60 * s);
|
||||
|
||||
auto font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED,
|
||||
ok ? FM_Mono : FM_Standard);
|
||||
int scrollbar_width = Environment->getSkin()->getSize(gui::EGDS_SCROLLBAR_SIZE);
|
||||
int max_cols = (rect.getWidth() - scrollbar_width - 10) / font->getDimension(L"x").Width;
|
||||
|
||||
text = wrap_rows(text, max_cols, true);
|
||||
|
||||
rect += topleft_client + v2s32(20 * s, ypos);
|
||||
IGUIEditBox *e = new GUIEditBoxWithScrollBar(utf8_to_wide(text).c_str(), true, Environment,
|
||||
this, ID_url, rect, m_tsrc, false, true);
|
||||
e->setMultiLine(true);
|
||||
e->setWordWrap(true);
|
||||
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
|
||||
e->setDrawBorder(true);
|
||||
e->setDrawBackground(true);
|
||||
e->setOverrideFont(font);
|
||||
e->drop();
|
||||
}
|
||||
|
||||
ypos += 80 * s;
|
||||
if (ok) {
|
||||
core::rect<s32> rect(0, 0, 100 * s, 40 * s);
|
||||
rect = rect + v2s32(size.X / 2 - 150 * s, ypos);
|
||||
GUIButton::addButton(Environment, rect, m_tsrc, this, ID_open,
|
||||
wstrgettext("Open").c_str());
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 100 * s, 40 * s);
|
||||
rect = rect + v2s32(size.X / 2 + 50 * s, ypos);
|
||||
GUIButton::addButton(Environment, rect, m_tsrc, this, ID_cancel,
|
||||
wstrgettext("Cancel").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GUIOpenURLMenu::drawMenu()
|
||||
{
|
||||
gui::IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
|
||||
video::SColor bgcolor(140, 0, 0, 0);
|
||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
#ifdef __ANDROID__
|
||||
getAndroidUIInput();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GUIOpenURLMenu::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT) {
|
||||
if ((event.KeyInput.Key == KEY_ESCAPE ||
|
||||
event.KeyInput.Key == KEY_CANCEL) &&
|
||||
event.KeyInput.PressedDown) {
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
|
||||
porting::open_url(url);
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.EventType == EET_GUI_EVENT) {
|
||||
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST &&
|
||||
isVisible()) {
|
||||
if (!canTakeFocus(event.GUIEvent.Element)) {
|
||||
infostream << "GUIOpenURLMenu: Not allowing focus change."
|
||||
<< std::endl;
|
||||
// Returning true disables focus change
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
|
||||
switch (event.GUIEvent.Caller->getID()) {
|
||||
case ID_open:
|
||||
porting::open_url(url);
|
||||
quitMenu();
|
||||
return true;
|
||||
case ID_cancel:
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Parent != nullptr && Parent->OnEvent(event);
|
||||
}
|
50
src/gui/guiOpenURL.h
Normal file
50
src/gui/guiOpenURL.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Part of Minetest
|
||||
Copyright (C) 2023 rubenwardy
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include <string>
|
||||
|
||||
class Client;
|
||||
class ISimpleTextureSource;
|
||||
|
||||
class GUIOpenURLMenu : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUIOpenURLMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
ISimpleTextureSource *tsrc, const std::string &url);
|
||||
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
*/
|
||||
void regenerateGui(v2u32 screensize);
|
||||
|
||||
void drawMenu();
|
||||
|
||||
bool OnEvent(const SEvent &event);
|
||||
|
||||
protected:
|
||||
std::wstring getLabelByID(s32 id) { return L""; }
|
||||
std::string getNameByID(s32 id) { return ""; }
|
||||
|
||||
private:
|
||||
ISimpleTextureSource *m_tsrc;
|
||||
std::string url;
|
||||
};
|
|
@ -34,7 +34,7 @@ public:
|
|||
virtual void disconnect() = 0;
|
||||
virtual void changePassword() = 0;
|
||||
virtual void changeVolume() = 0;
|
||||
|
||||
virtual void showOpenURLDialog(const std::string &url) = 0;
|
||||
virtual void signalKeyConfigChange() = 0;
|
||||
};
|
||||
|
||||
|
@ -108,44 +108,47 @@ public:
|
|||
MainGameCallback() = default;
|
||||
virtual ~MainGameCallback() = default;
|
||||
|
||||
virtual void exitToOS()
|
||||
void exitToOS() override
|
||||
{
|
||||
shutdown_requested = true;
|
||||
}
|
||||
|
||||
virtual void disconnect()
|
||||
void disconnect() override
|
||||
{
|
||||
disconnect_requested = true;
|
||||
}
|
||||
|
||||
virtual void changePassword()
|
||||
void changePassword() override
|
||||
{
|
||||
changepassword_requested = true;
|
||||
}
|
||||
|
||||
virtual void changeVolume()
|
||||
void changeVolume() override
|
||||
{
|
||||
changevolume_requested = true;
|
||||
}
|
||||
|
||||
virtual void keyConfig()
|
||||
void keyConfig() override
|
||||
{
|
||||
keyconfig_requested = true;
|
||||
}
|
||||
|
||||
virtual void signalKeyConfigChange()
|
||||
void signalKeyConfigChange() override
|
||||
{
|
||||
keyconfig_changed = true;
|
||||
}
|
||||
|
||||
void showOpenURLDialog(const std::string &url) override {
|
||||
show_open_url_dialog = url;
|
||||
}
|
||||
|
||||
bool disconnect_requested = false;
|
||||
bool changepassword_requested = false;
|
||||
bool changevolume_requested = false;
|
||||
bool keyconfig_requested = false;
|
||||
bool shutdown_requested = false;
|
||||
|
||||
bool keyconfig_changed = false;
|
||||
std::string show_open_url_dialog = "";
|
||||
};
|
||||
|
||||
extern MainGameCallback *g_gamecallback;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue