Skip to content

Commit

Permalink
Integrate RmlUi (#1415)
Browse files Browse the repository at this point in the history
* Add RmlUi to build system

Not all is ready and there will be more work needed on build system, but
the basic integration of RmlUi Core is done.

- I've not created and linked any of the provided GL3 or SDL backends,
  that needs separate work
- Lua bindings are blocked on mikke89/RmlUi#302
  we need to find some workaround for that
- Tracy integration is blocked on mikke89/RmlUi#516

* render first light

* remove sdl2_image

* VFS system integration

* rmlui lua and sdl integration

* rmlui reload support and better luaui integration

* delete extra file

* Temporarily switch to RmlUi branch with tracy integration fixed

* data model change detection bad edition

* fix for chobby support, better binds, translation table

* key event handling, luaui reloading, rmlsol bug fixes

* remove rml demo files

* create contexts lua side

* clean up comments

* allow multiple rmlui contexts

* pull out inner loop of processevent calls

* add comment to make it clear certain events are not captured

* Update RmlUi to latest git version

* headless building and lua support

* add lunasvg submodule

* update lunasvg version

* reset build option

* didnt need to build lunasvg as a shared lib

* fix: uint is not standard and breaks mingw compile

* Rework RmlSolLua integration into build

This fixes the build under mingw that didn't work because of broken
linking.

- Cleanup messu include_target_directories replacing with proper
  linking of targets. Had to fix the include target directories for
  lua lib
- Undo merging of the public and private interfaces, and separate
  public interface of the library
- Stop appending the include dir directly to engine include dirs
  and stop using absolute path based references when referencing.

* Fix RmlSolLua compilation under MSVC

- Designated initializers are C++20 feature, so we need to bump
  to that version
- Element.cpp generates huge amount of sections, so adding /bigobj
  switch for it.

* Document the mouse press check

* Change size() < 1 to empty. Move setActive after processContext loop

* Deeper MouseHandler integration for processing mouse events

* Fix software rendered mouse cursor

* Missed half of the move of this comment

* make lua binding for RemoveContext

* do less bullying of the RmlSolLua lib
instead set up our own plugin to keep track of created/removed contexts

* attempt to add thread safety to RmlGui::data->context accesses
allow RmlUI to properly set cursors
have RmlUI input events take precedence after other inputs (but after regular Lua input)

* have RmlGui store what cursor it would like (or "" to cede control of it) and have the system check that.
rename own custom Platform_SDL stuff to Platform_RTS
clean up code here and there, undo changes to random files

* Platform_RTS ended up being disliked, so re-renamed stuff to "RmlSystemInterface" and the namespace to "RmlSDLSpring"

* check Rml input first

* fix a singular character to be lowercase blargh

* fix spelling

* reference fix

* use cbitmap texture binding

* spaces to tabs with clang-format

* mask lua macros

* Remove unused variable

* add an explicit removeLua call

* accurate tracking of lua added items

* move general Rml::Context watching plugin outside of Lua init/destroy
get rid of never-going-to-be-used code in RmlUi_Renderer_GL3
rename file/class to RmlUi_Renderer_GL3_Spring since it's now more deeply integrated with Spring
move VFSInterface to own files

* left some mess in, sorry

* Remove unnecessary includes

* create new <texture> element which hooks into the existing LuaOpenGL/Texture code to be able to reuse already made textures

* what if we just kept the texture handle away from Rml

* clearer and more readable element tag name

* misc changes

- remove lua mentions in rml code
- initial insertion of lua work from loveridge/rmlui-integration-fork
- use translation table

* copyright auto insert go brrr too hard

* leave a note on the UI reload

* move RmlSolLua out of the lib folder so that it can integrate more with the rest of the Spring engine

* according to a tutorial, the proper implementation of the iterator concept looks like this

* minor cleanup

* minor cleanup as I investigate how Data Models work

* use the configured lua_Number type instead of blindly assuming double

* beginning refactor of RenderInterface

* we can just use `glPush/PopAttrib()` here

* narrow down to just the attrib bits needed

* misc changes

- give RmlUi lib code a double instead of a float for system time since that's what it's asking for
  make use of the Rml DataModel stricter
- (disallow adding keys to an existing DataModel)

* drop element render hook

* untested custom Rml Decorator: 'lua-render' (name not final)

* rml lua-render decorator barely functional
use like so <div style="decorator: lua-render(callback_name_here)"/>

* remove redundant Log binding from SolLua
Get the Rml_MathTypes_Conversions.h working

* many changes

* Actually add submodules

* Fix g++ compilation

* just use one shader program, copy in documentation comments to make working on the render interface easier

* SolLua  binding "low hanging fruit" cleanup

* make all the RmlUi Lua bindings into a "RmlUi" namespace table to better the other lua bindings
add setup files to cont folder

* add Mouse Cursor Aliases feature

* use <texture> instead of <lua-texture> as LuaTextureElement tag name

* add mouse cursor alias stuff to example rml_setup.lua

* touch up rml_setup.lua

* without the render_interface being 100% finalized, it is too early for DecoratorLuaRender to exist (made fork that still has it though)

* get rid of extraneous whitespace changes

* replace space indenting with tab indenting

* RmlGui::Initialize gets own globals + allow Reload to init

* clean up/fixing of init/shutdown/Context removal

remove SDLWindow/Context ref since they are not used in the end
keep Rml Interfaces alive until after Rml::Shutdown is called()
defer removal of Rml::Context objects,
this allows for the removal of the guarding mutex

* tidy event handling + more

include context removal in #ifndef HEADLESS
comment on why Update() is ran in RemoveLua()

* Replace README.md with notice of where the code came from

* Add MIT license info to almost all Rml/SolLua source files

* add support for border-radius property to ElementLuaTexture.cpp

* recalc geometry when rect attribute is set to (blank)

* create lua hook for the RmlUi debugger

* context ordering, rm PasThrPlugin, data > state

Allow changing the order of contexts in the state->contexts vector
clicked context is brought to front, but debug context (if exists) is always at the very front

PassThroughPlugin was a redundant abstraction

BackendState sounds better now that it's a class and not a struct

* fix debugger use-after-free crash

* remove contexts last in update

* fix crash in StyleProxy iteration

* add returns from element hierarchy manip bindings

* code style tidying

* allow nil and blank strings to clear a property

* Fix whitespace

* Remove lua mask/restore macros as fix is on BAR105

* bump to rmlui repo latest

* use renamed RMLUI_ option

* use new RmlUi target names

* Create Uniform*v variants that take a "count" parameter

* Initial update to RmlUi 6. Code is compiling.

* interface with Shader API correctly

* better init logging

* update "is initialized" check

* code/formatting cleanup

* sidestep the RmlUi resource handling to render the texture and geometry directly

* remove outdated comment

* fix use-after-free issue by removing debugger before shutting down lua

* size of a non-table is 0 actually

* RmlUi log pretty useful actually

* Don't deactivate the shader if TexturePostprocess is used

* ensure that RmlUi can be loaded after changing UI Size option

* bump RmlUi for flexbox gap feature

* inline rgba Colour binding macro

* remove outdated commented-out code

* update headless render interface stub

* (whitespace) correct accidental tab

* update RmlUi to official 6.0 release

* update backend files with updates made to the originals

---------

Co-authored-by: loveridge <[email protected]>
Co-authored-by: sprunk <[email protected]>
Co-authored-by: ChrisClark13 <[email protected]>
Co-authored-by: LHoG <[email protected]>
  • Loading branch information
5 people authored Sep 1, 2024
1 parent 2ef0794 commit b260645
Show file tree
Hide file tree
Showing 66 changed files with 7,512 additions and 28 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@
[submodule "rts/lib/cereal"]
path = rts/lib/cereal
url = https://github.com/USCiLab/cereal.git
[submodule "rts/lib/RmlUi"]
path = rts/lib/RmlUi
url = https://github.com/mikke89/RmlUi.git
[submodule "rts/lib/lunasvg"]
path = rts/lib/lunasvg
url = https://github.com/sammycage/lunasvg.git
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ else()
set(JAVA_COMPILE_FLAG_CONDITIONAL "-g:lines,source")
endif()

# By default, the libraries that don't explicily specify SHARED/STATIC are build statically.
# See https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html
option(BUILD_SHARED_LIBS "Build shared (dynamic) libraries" OFF)

# Tell windows to pass arguments around in @response files
# (32k arg limit workaround)
Expand Down
3 changes: 2 additions & 1 deletion cont/LuaUI/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

Spring.SendCommands({"ctrlpanel " .. LUAUI_DIRNAME .. "ctrlpanel.txt"})

VFS.Include(LUAUI_DIRNAME .. 'utils.lua', utilFile)
VFS.Include(LUAUI_DIRNAME .. "rml_setup.lua", nil, VFS.ZIP)
VFS.Include(LUAUI_DIRNAME .. 'utils.lua', nil, VFS.ZIP)

include("setupdefs.lua")
include("savetable.lua")
Expand Down
27 changes: 27 additions & 0 deletions cont/LuaUI/rml_setup.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- file: rml.lua
-- brief: RmlUi Setup
-- author: lov + ChrisFloofyKitsune
--
-- Copyright (C) 2024.
-- Licensed under the terms of the GNU GPL, v2 or later.

if (RmlGuard or not RmlUi) then
return
end
RmlGuard = true

-- Load fonts
RmlUi.LoadFontFace("Fonts/FreeMonoBold.ttf", true)

-- Mouse Cursor Aliases
--[[
These let standard CSS cursor names be used when doing styling.
If a cursor set via RCSS does not have an alias, it is unchanged.
CSS cursor list: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
RmlUi documentation: https://mikke89.github.io/RmlUiDoc/pages/rcss/user_interface.html#cursor
]]

-- when "cursor: normal" is set via RCSS, "cursornormal" will be sent to the engine
RmlUi.SetMouseCursorAlias("default", 'cursornormal')
-- for command cursors, use the command name
-- RmlUi.SetMouseCursorAlias("pointer", 'Move')
1 change: 1 addition & 0 deletions cont/LuaUI/system.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ if (System == nil) then
CMD = CMD,
CMDTYPE = CMDTYPE,
LOG = LOG,
RmlUi = RmlUi,

UnitDefs = UnitDefs,
UnitDefNames = UnitDefNames,
Expand Down
3 changes: 3 additions & 0 deletions rts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ add_subdirectory(Lua)
add_subdirectory(ExternalAI)
add_subdirectory(Rendering)
add_subdirectory(aGui)
add_subdirectory(Rml)
add_subdirectory(Menu)
add_subdirectory(Map)
add_subdirectory(Net)
Expand All @@ -152,6 +153,8 @@ make_global_var(engineSources
${sources_engine_Map}
${sources_engine_Rendering}
${sources_engine_aGui}
${sources_engine_Rml}
${sources_engine_RmlSolLua}
${sources_engine_Menu}
${sources_engine_System}
${sources_engine_ExternalAI}
Expand Down
16 changes: 16 additions & 0 deletions rts/Game/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "Rendering/GL/myGL.h"

#include <Rml/Backends/RmlUi_Backend.h>
#include <RmlUi/Core.h>
#include "Game.h"
#include "Camera.h"
#include "CameraHandler.h"
Expand Down Expand Up @@ -296,6 +298,7 @@ CGame::~CGame()
ENTER_SYNCED_CODE();
LOG("[Game::%s][1]", __func__);

RmlGui::Shutdown();
helper->Kill();
KillLua(true);
KillMisc();
Expand Down Expand Up @@ -828,6 +831,8 @@ void CGame::LoadInterface()
GameSetupDrawer::Disable();
GameSetupDrawer::Enable();
}

RmlGui::Initialize();
}

void CGame::LoadLua(bool dryRun, bool onlyUnsynced)
Expand Down Expand Up @@ -1105,6 +1110,9 @@ int CGame::KeyPressed(int keyCode, int scanCode, bool isRepeat)

lastActionList = keyBindings.GetActionList(curKeyCodeChain, curScanCodeChain);

if (RmlGui::ProcessKeyPressed(keyCode, scanCode, isRepeat))
return 0;

if (gameTextInput.ConsumePressedKey(keyCode, scanCode, lastActionList))
return 0;

Expand Down Expand Up @@ -1145,6 +1153,9 @@ int CGame::KeyPressed(int keyCode, int scanCode, bool isRepeat)
int CGame::KeyReleased(int keyCode, int scanCode)
{
RECOIL_DETAILED_TRACY_ZONE;
if (RmlGui::ProcessKeyReleased(keyCode, scanCode))
return 0;

if (gameTextInput.ConsumeReleasedKey(keyCode, scanCode))
return 0;

Expand Down Expand Up @@ -1180,6 +1191,9 @@ int CGame::KeyMapChanged()
int CGame::TextInput(const std::string& utf8Text)
{
RECOIL_DETAILED_TRACY_ZONE;
if (RmlGui::ProcessTextInput(utf8Text))
return 0;

if (eventHandler.TextInput(utf8Text))
return 0;

Expand Down Expand Up @@ -1473,6 +1487,7 @@ bool CGame::Draw() {
if (UpdateUnsynced(currentTimePreUpdate))
return false;

RmlGui::Update();
const spring_time currentTimePreDraw = spring_gettime();

SCOPED_SPECIAL_TIMER("Draw");
Expand Down Expand Up @@ -1560,6 +1575,7 @@ bool CGame::Draw() {
DrawInputReceivers();
DrawInputText();
DrawInterfaceWidgets();
RmlGui::RenderFrame();
mouse->DrawCursor();

eventHandler.DrawScreenPost();
Expand Down
2 changes: 2 additions & 0 deletions rts/Game/UI/GuiHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "GuiHandler.h"

#include <Rml/Backends/RmlUi_Backend.h>
#include "CommandColors.h"
#include "KeyBindings.h"
#include "KeyCodes.h"
Expand Down Expand Up @@ -174,6 +175,7 @@ bool CGuiHandler::EnableLuaUI(bool enableCommand)
}
}

RmlGui::Reload();
CLuaUI::ReloadHandler();

if (luaUI != nullptr) {
Expand Down
7 changes: 6 additions & 1 deletion rts/Game/UI/InputReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "InputReceiver.h"
#include "Lua/LuaInputReceiver.h"
#include "Rendering/GL/myGL.h"
#include "Rml/Backends/RmlUi_Backend.h"
#include "System/Rectangle.h"

#include "System/Misc/TracyDefs.h"
Expand Down Expand Up @@ -75,7 +76,11 @@ void CInputReceiver::DrawReceivers()
CInputReceiver* CInputReceiver::GetReceiverAt(int x, int y)
{
RECOIL_DETAILED_TRACY_ZONE;
// always ask Lua first
// check RmlUI first
if (RmlGui::IsMouseInteractingWith())
return RmlGui::GetInputReceiver();

// check lua second
if (luaInputReceiver != nullptr && luaInputReceiver->IsAbove(x, y))
return luaInputReceiver;

Expand Down
41 changes: 40 additions & 1 deletion rts/Game/UI/MouseHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "System/StringUtil.h"
#include "System/Input/KeyInput.h"
#include "System/Input/MouseInput.h"
#include "Rml/Backends/RmlUi_Backend.h"

#include "System/Misc/TracyDefs.h"

Expand Down Expand Up @@ -281,6 +282,13 @@ void CMouseHandler::MouseMove(int x, int y, int dx, int dy)
if (game != nullptr && !game->IsGameOver())
playerHandler.Player(gu->myPlayerNum)->currentStats.mousePixels += movedPixels;

/* Only want to give a mouse event to RmlUI if the mouse isn't currently performing a drag.
* Otherwise box selections get stuck when the mouse goes over an Rml element.
* Flags that ButtonPressed() checks are not set when clicking on Rml element. */
if (!ButtonPressed() && RmlGui::ProcessMouseMove(x, lasty, dx, dy, activeButtonIdx)) {
return;
}

if (activeReceiver != nullptr)
activeReceiver->MouseMove(x, lasty, dx, dy, activeButtonIdx);

Expand Down Expand Up @@ -309,7 +317,12 @@ void CMouseHandler::MousePress(int x, int y, int button)
if (game != nullptr && !game->IsGameOver())
playerHandler.Player(gu->myPlayerNum)->currentStats.mouseClicks++;

ButtonPressEvt& bp = buttons[activeButtonIdx = button];
if (RmlGui::ProcessMousePress(x, y, button)) {
return;
}

activeButtonIdx = button;
ButtonPressEvt& bp = buttons[activeButtonIdx];
bp.chorded = (buttons[SDL_BUTTON_LEFT].pressed || buttons[SDL_BUTTON_RIGHT].pressed);
bp.pressed = true;
bp.time = gu->gameTime;
Expand All @@ -319,6 +332,8 @@ void CMouseHandler::MousePress(int x, int y, int button)
bp.dir = (dir = GetCursorCameraDir(x, y));
bp.movement = 0;

pressedBitMask |= 1 << button;

if (activeReceiver != nullptr && activeReceiver->MousePress(x, y, button))
return;

Expand Down Expand Up @@ -379,6 +394,10 @@ bool CMouseHandler::GetSelectionBoxVertices(float3& bl, float3& br, float3& tl,
if (activeReceiver != nullptr)
return false;

if (RmlGui::IsMouseInteractingWith()) {
return false;
}

if (gu->fpsMode)
return false;

Expand Down Expand Up @@ -477,12 +496,17 @@ void CMouseHandler::MouseRelease(int x, int y, int button)
dir = GetCursorCameraDir(x, y);

buttons[button].pressed = false;
pressedBitMask &= ~(1 << button);

if (inMapDrawer != nullptr && inMapDrawer->IsDrawMode()) {
inMapDrawer->MouseRelease(x, y, button);
return;
}

if (RmlGui::ProcessMouseRelease(x, y, button)) {
return;
}

if (activeReceiver != nullptr) {
activeReceiver->MouseRelease(x, y, button);

Expand Down Expand Up @@ -557,10 +581,18 @@ void CMouseHandler::MouseRelease(int x, int y, int button)
}
}

bool CMouseHandler::ButtonPressed()
{
return pressedBitMask > 0;
}

void CMouseHandler::MouseWheel(float delta)
{
RECOIL_DETAILED_TRACY_ZONE;
if (RmlGui::ProcessMouseWheel(delta)) {
return;
}

if (eventHandler.MouseWheel(delta > 0.0f, delta))
return;

Expand Down Expand Up @@ -691,6 +723,13 @@ std::string CMouseHandler::GetCurrentTooltip() const
void CMouseHandler::Update()
{
RECOIL_DETAILED_TRACY_ZONE;
// Rml is very polite about asking for changes to the cursor
// so let's make sure it's not ignored!
if (RmlGui::IsMouseInteractingWith())
// if the cursor string is empty, then Rml is cedeing control of it
if (auto& rmlCursor = RmlGui::GetMouseCursor(); !rmlCursor.empty())
queuedCursorName = rmlCursor;

SetCursor(queuedCursorName);

if (!hideCursor) {
Expand Down
3 changes: 3 additions & 0 deletions rts/Game/UI/MouseHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class CMouseHandler

bool GetSelectionBoxVertices(float3& bl, float3& br, float3& tl, float3& tr) const;

bool ButtonPressed();

private:
int2 GetViewMouseCenter() const;
void SetCursor(const std::string& cmdName, const bool forceRebind = false);
Expand Down Expand Up @@ -116,6 +118,7 @@ class CMouseHandler
bool wasLocked = false;
bool offscreen = false;
bool mmbScroll = false;
uint32_t pressedBitMask = 0;

private:
bool hideCursor = true;
Expand Down
9 changes: 9 additions & 0 deletions rts/Lua/LuaHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "Game/UI/KeySet.h"
#include "Game/UI/MiniMap.h"
#include "Rendering/GlobalRendering.h"
#include "Rml/Backends/RmlUi_Backend.h"
#include "Sim/Misc/GlobalSynced.h"
#include "Sim/Misc/TeamHandler.h"
#include "Sim/Projectiles/Projectile.h"
Expand Down Expand Up @@ -174,6 +175,10 @@ void CLuaHandle::KillLua(bool inFreeHandler)
if (inFreeHandler)
Shutdown();

if(rmlui) {
RmlGui::RemoveLua();
}

// 3. delete the lua_State
//
// must be done here: if called from a ctor, we want the
Expand Down Expand Up @@ -3921,6 +3926,10 @@ int CLuaHandle::CallOutUpdateCallIn(lua_State* L)
return 0;
}

void CLuaHandle::InitializeRmlUi()
{
rmlui = RmlGui::InitializeLua(L);
}

/******************************************************************************/
/******************************************************************************/
2 changes: 2 additions & 0 deletions rts/Lua/LuaHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ class CLuaHandle : public CEventClient
void RunDrawCallIn(const LuaHashString& hs);

void DrawObjectsLua(std::initializer_list<bool> bools, const char* func);
void InitializeRmlUi();
protected:
bool rmlui = false;
bool userMode = false;
bool killMe = false; // set for handles that fail to RunCallIn

Expand Down
1 change: 1 addition & 0 deletions rts/Lua/LuaUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ CLuaUI::CLuaUI()
KillLua();
return;
}
InitializeRmlUi();

lua_settop(L, 0);
if (!LoadCode(L, std::move(code), file)) {
Expand Down
Loading

0 comments on commit b260645

Please sign in to comment.