Skip to content

Commit

Permalink
Add button_url[] and hypertext element to allow mods to open web pages (
Browse files Browse the repository at this point in the history
#13825)

Fixes #12500
  • Loading branch information
rubenwardy committed Mar 24, 2024
1 parent 6c4a110 commit 24cc33e
Show file tree
Hide file tree
Showing 17 changed files with 530 additions and 37 deletions.
6 changes: 1 addition & 5 deletions builtin/mainmenu/tab_about.lua
Expand Up @@ -158,7 +158,7 @@ return {
"style[label_button;border=false]" ..
"button[0.1,3.4;5.3,0.5;label_button;" ..
core.formspec_escape(version.project .. " " .. version.string) .. "]" ..
"button[1.5,4.1;2.5,0.8;homepage;minetest.net]" ..
"button_url[1.5,4.1;2.5,0.8;homepage;minetest.net;https://www.minetest.net/]" ..
"hypertext[5.5,0.25;9.75,6.6;credits;" .. minetest.formspec_escape(hypertext) .. "]"

-- Render information
Expand Down Expand Up @@ -188,10 +188,6 @@ return {
end,

cbf_button_handler = function(this, fields, name, tabdata)
if fields.homepage then
core.open_url("https://www.minetest.net")
end

if fields.share_debug then
local path = core.get_user_path() .. DIR_DELIM .. "debug.txt"
core.share_file(path)
Expand Down
19 changes: 19 additions & 0 deletions doc/lua_api.md
Expand Up @@ -3017,6 +3017,16 @@ Elements
centered on `H`. With the new coordinate system, `H` will modify the height.
* `label` is the text on the button

### `button_url[<X>,<Y>;<W>,<H>;<name>;<label>;<url>]`

* Clickable button. When clicked, fields will be sent and the user will be given the
option to open the URL in a browser.
* With the old coordinate system, buttons are a set height, but will be vertically
centered on `H`. With the new coordinate system, `H` will modify the height.
* To make this into an `image_button`, you can use formspec styling.
* `label` is the text on the button.
* `url` must be a valid web URL, starting with `http://` or `https://`.

### `image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]`

* `texture name` is the filename of an image
Expand All @@ -3043,6 +3053,11 @@ Elements
* When clicked, fields will be sent and the form will quit.
* Same as `button` in all other respects.

### `button_url_exit[<X>,<Y>;<W>,<H>;<name>;<label>;<url>]`

* When clicked, fields will be sent and the form will quit.
* Same as `button_url` in all other respects.

### `image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]`

* When clicked, fields will be sent and the form will quit.
Expand Down Expand Up @@ -3538,11 +3553,13 @@ Changes the style of the text.
Sets global style.

Global only styles:

* `background`: Text background, a `colorspec` or `none`.
* `margin`: Page margins in pixel.
* `valign`: Text vertical alignment (`top`, `middle`, `bottom`).

Inheriting styles (affects child elements):

* `color`: Default text color. Given color is a `colorspec`.
* `hovercolor`: Color of <action> tags when mouse is over.
* `size`: Default text size.
Expand All @@ -3556,6 +3573,7 @@ tags appear.
`<tag name=... color=... hovercolor=... font=... size=...>`

Defines or redefines tag style. This can be used to define new tags.

* `name`: Name of the tag to define or change.
* `color`: Text color. Given color is a `colorspec`.
* `hovercolor`: Text color when element hovered (only for `action` tags). Given color is a `colorspec`.
Expand Down Expand Up @@ -3588,6 +3606,7 @@ Other tags can be added using `<tag ...>` tag.
Make that text a clickable text triggering an action.

* `name`: Name of the action (mandatory).
* `url`: URL to open when the action is triggered (optional).

When clicked, the formspec is send to the server. The value of the text field
sent to `on_player_receive_fields` will be "action:" concatenated to the action
Expand Down
8 changes: 6 additions & 2 deletions games/devtest/mods/testformspec/formspec.lua
@@ -1,12 +1,15 @@
local color = minetest.colorize

-- \208\176 is a cyrillic small a
local unsafe_url = minetest.formspec_escape("https://u:p@wikipedi\208\176.org:1233/heIIoll?a=b#c")

local clip_fs = [[
style_type[label,button,image_button,item_image_button,
tabheader,scrollbar,table,animated_image
,field,textarea,checkbox,dropdown;noclip=%c]
label[0,0;A clipping test]
button[0,1;3,0.8;clip_button;A clipping test]
button_url[0,1;3,0.8;clip_button;A clipping test;]] .. unsafe_url .. [[]
image_button[0,2;3,0.8;testformspec_button_image.png;clip_image_button;A clipping test]
item_image_button[0,3;3,0.8;testformspec:item;clip_item_image_button;A clipping test]
tabheader[0,4.7;3,0.63;clip_tabheader;Clip,Test,Text,Tabs;1;false;false]
Expand Down Expand Up @@ -92,6 +95,7 @@ This is a normal text.
<t_green>color=green</t_green>
Action: <action name=color><t_green>color=green</t_green></action>
Action: <action name=hovercolor><t_hover>hovercolor=yellow</t_hover></action>
Action URL: <action name=open url=https://example.com/?a=b#c>open URL</action>
<t_size>size=24</t_size>
<t_mono>font=mono</t_mono>
<t_multi>color=green font=mono size=24</t_multi>
Expand Down Expand Up @@ -145,7 +149,7 @@ local hypertext_fs = "hypertext[0,0;11,9;hypertext;"..minetest.formspec_escape(h
local style_fs = [[
style[one_btn1;bgcolor=red;textcolor=yellow;bgcolor_hovered=orange;
bgcolor_pressed=purple]
button[0,0;2.5,0.8;one_btn1;Button]
button_url_exit[0,0;2.5,0.8;one_btn1;Button;]] .. unsafe_url .. [[]
style[one_btn2;border=false;textcolor=cyan] ]]..
"button[0,1.05;2.5,0.8;one_btn2;Text " .. color("#FF0", "Yellow") .. [[]
Expand Down
7 changes: 7 additions & 0 deletions src/client/game.cpp
Expand Up @@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gui/guiFormSpecMenu.h"
#include "gui/guiKeyChangeMenu.h"
#include "gui/guiPasswordChange.h"
#include "gui/guiOpenURL.h"
#include "gui/guiVolumeChange.h"
#include "gui/mainmenumanager.h"
#include "gui/profilergraph.h"
Expand Down Expand Up @@ -1815,6 +1816,12 @@ inline bool Game::handleCallbacks()
g_gamecallback->keyconfig_requested = false;
}

if (!g_gamecallback->show_open_url_dialog.empty()) {
(new GUIOpenURLMenu(guienv, guiroot, -1,
&g_menumgr, texture_src, g_gamecallback->show_open_url_dialog))->drop();
g_gamecallback->show_open_url_dialog.clear();
}

if (g_gamecallback->keyconfig_changed) {
input->keycache.populate(); // update the cache with new settings
g_gamecallback->keyconfig_changed = false;
Expand Down
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -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
Expand Down
23 changes: 20 additions & 3 deletions src/gui/guiFormSpecMenu.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/gui/guiFormSpecMenu.h
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions src/gui/guiHyperText.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 24cc33e

Please sign in to comment.