Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js (revision 23102)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js (revision 23103)
@@ -1,142 +1,150 @@
/**
* This class sets up the main menu buttons, animates submenu that opens when
* clicking on category buttons, assigns the defined actions and hotkeys to every button.
*/
class MainMenuItemHandler
{
constructor(menuItems)
{
this.menuItems = menuItems;
this.lastTickTime = Date.now();
this.mainMenu = Engine.GetGUIObjectByName("mainMenu");
this.mainMenuButtons = Engine.GetGUIObjectByName("mainMenuButtons");
this.submenu = Engine.GetGUIObjectByName("submenu");
this.submenuButtons = Engine.GetGUIObjectByName("submenuButtons");
this.MainMenuPanelRightBorderTop = Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop");
this.MainMenuPanelRightBorderBottom = Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom");
this.setupMenuButtons(this.mainMenuButtons.children, this.menuItems);
this.setupHotkeys(this.menuItems);
- this.mainMenu.onTick = this.onTick.bind(this);
Engine.GetGUIObjectByName("closeMenuButton").onPress = this.closeSubmenu.bind(this);
}
setupMenuButtons(buttons, menuItems)
{
buttons.forEach((button, i) => {
let item = menuItems[i];
button.hidden = !item;
if (button.hidden)
return;
button.size = new GUISize(
0, (this.ButtonHeight + this.Margin) * i,
0, (this.ButtonHeight + this.Margin) * i + this.ButtonHeight,
0, 0, 100, 0);
button.caption = item.caption;
button.tooltip = item.tooltip;
button.enabled = item.enabled === undefined || item.enabled;
button.onPress = () => {
this.closeSubmenu();
if (item.onPress)
item.onPress();
else
this.openSubmenu(i);
};
button.hidden = false;
});
if (buttons.length < menuItems.length)
error("GUI page has space for " + buttons.length + " menu buttons, but " + menuItems.length + " items are provided!");
}
setupHotkeys(menuItems)
{
for (let i in menuItems)
{
let item = menuItems[i];
if (item.onPress && item.hotkey)
Engine.SetGlobalHotkey(item.hotkey, () => {
this.closeSubmenu();
item.onPress();
});
if (item.submenu)
this.setupHotkeys(item.submenu);
}
}
openSubmenu(i)
{
this.setupMenuButtons(this.submenuButtons.children, this.menuItems[i].submenu);
let top = this.mainMenuButtons.size.top + this.mainMenuButtons.children[i].size.top;
this.submenu.size = new GUISize(
this.submenu.size.left, top - this.Margin,
this.submenu.size.right, top + (this.ButtonHeight + this.Margin) * this.menuItems[i].submenu.length);
this.submenu.hidden = false;
{
let size = this.MainMenuPanelRightBorderTop.size;
size.bottom = this.submenu.size.top + this.Margin;
size.rbottom = 0;
this.MainMenuPanelRightBorderTop.size = size;
}
{
let size = this.MainMenuPanelRightBorderBottom.size;
size.top = this.submenu.size.bottom;
this.MainMenuPanelRightBorderBottom.size = size;
}
+
+ // Start animation
+ this.lastTickTime = Date.now();
+ this.mainMenu.onTick = this.onTick.bind(this);
}
closeSubmenu()
{
this.submenu.hidden = true;
this.submenu.size = this.mainMenu.size;
let size = this.MainMenuPanelRightBorderTop.size;
size.top = 0;
size.bottom = 0;
size.rbottom = 100;
this.MainMenuPanelRightBorderTop.size = size;
}
onTick()
{
let now = Date.now();
+ if (now == this.lastTickTime)
+ return;
let maxOffset = this.mainMenu.size.right - this.submenu.size.left;
let offset = Math.min(this.MenuSpeed * (now - this.lastTickTime), maxOffset);
this.lastTickTime = now;
- if (this.submenu.hidden || offset <= 0)
+ if (this.submenu.hidden || !offset)
+ {
+ delete this.mainMenu.onTick;
return;
+ }
let size = this.submenu.size;
size.left += offset;
size.right += offset;
this.submenu.size = size;
}
}
/**
* Vertical size per button.
*/
MainMenuItemHandler.prototype.ButtonHeight = 28;
/**
* Distance between consecutive buttons.
*/
MainMenuItemHandler.prototype.Margin = 4;
/**
* Collapse / expansion speed in pixels per milliseconds used when animating the button menu size.
*/
MainMenuItemHandler.prototype.MenuSpeed = 1.2;
Index: ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js (revision 23102)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js (revision 23103)
@@ -1,68 +1,67 @@
class SplashScreenHandler
{
constructor(initData, hotloadData)
{
this.showSplashScreen = hotloadData ? hotloadData.showSplashScreen : initData && initData.isStartup;
this.mainMenuPage = Engine.GetGUIObjectByName("mainMenuPage");
this.mainMenuPage.onTick = this.onFirstTick.bind(this);
}
getHotloadData()
{
// Only show splash screen(s) once at startup, but not again after hotloading
return {
"showSplashScreen": this.showSplashScreen
};
}
// Don't call this from the init function in order to not crash when opening the new page on init on hotloading
// and not possibly crash when opening the new page on init and throwing a JS error.
onFirstTick()
{
if (this.showSplashScreen)
this.openPage();
- // TODO: support actually deleting the handler
- this.mainMenuPage.onTick = () => {};
+ delete this.mainMenuPage.onTick;
}
openPage()
{
this.showSplashScreen = false;
if (Engine.ConfigDB_GetValue("user", "gui.splashscreen.enable") === "true" ||
Engine.ConfigDB_GetValue("user", "gui.splashscreen.version") < Engine.GetFileMTime("gui/splashscreen/splashscreen.txt"))
Engine.PushGuiPage("page_splashscreen.xml", {}, this.showRenderPathMessage);
else
this.showRenderPathMessage();
}
showRenderPathMessage()
{
// Warn about removing fixed render path
if (Engine.Renderer_GetRenderPath() != "fixed")
return;
messageBox(
600, 300,
"[font=\"sans-bold-16\"]" +
sprintf(translate("%(warning)s You appear to be using non-shader (fixed function) graphics. This option will be removed in a future 0 A.D. release, to allow for more advanced graphics features. We advise upgrading your graphics card to a more recent, shader-compatible model."), {
"warning": coloredText("Warning:", "200 20 20")
}) +
"\n\n" +
// Translation: This is the second paragraph of a warning. The
// warning explains that the user is using “non-shader“ graphics,
// and that in the future this will not be supported by the game, so
// the user will need a better graphics card.
translate("Please press \"Read More\" for more information or \"OK\" to continue."),
translate("WARNING!"),
[translate("OK"), translate("Read More")],
[
null,
() => {
Engine.OpenURL("https://www.wildfiregames.com/forum/index.php?showtopic=16734");
}
]);
}
}
Index: ps/trunk/binaries/data/mods/public/gui/session/Menu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/Menu.js (revision 23102)
+++ ps/trunk/binaries/data/mods/public/gui/session/Menu.js (revision 23103)
@@ -1,120 +1,119 @@
/**
* This class constructs and positions the menu buttons and assigns the handlers defined in MenuButtons.
*/
class Menu
{
constructor(pauseControl, playerViewControl, chat)
{
this.menuButton = Engine.GetGUIObjectByName("menuButton");
this.menuButton.onPress = this.toggle.bind(this);
registerHotkeyChangeHandler(this.rebuild.bind(this));
this.isOpen = false;
this.lastTick = undefined;
this.menuButtonPanel = Engine.GetGUIObjectByName("menuButtonPanel");
let menuButtons = this.menuButtonPanel.children;
this.margin = menuButtons[0].size.top;
this.buttonHeight = menuButtons[0].size.bottom;
let handlerNames = this.getHandlerNames();
if (handlerNames.length > menuButtons.length)
throw new Error(
"There are " + handlerNames.length + " menu buttons defined, " +
"but only " + menuButtons.length + " objects!");
this.buttons = handlerNames.map((handlerName, i) => {
let handler = new MenuButtons.prototype[handlerName](menuButtons[i], pauseControl, playerViewControl, chat);
this.initButton(handler, menuButtons[i], i);
return handler;
});
this.endPosition = this.margin + this.buttonHeight * (1 + handlerNames.length);
let size = this.menuButtonPanel.size;
size.top = -this.endPosition;
size.bottom = 0;
this.menuButtonPanel.size = size;
}
rebuild()
{
this.menuButton.tooltip = sprintf(translate("Press %(hotkey)s to toggle this menu."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.menuButton.hotkey),
});
}
/**
* This function may be overwritten to change the button order.
*/
getHandlerNames()
{
return Object.keys(MenuButtons.prototype);
}
toggle()
{
this.isOpen = !this.isOpen;
this.startAnimation();
}
close()
{
this.isOpen = false;
this.startAnimation();
}
initButton(handler, button, i)
{
button.onPress = () => {
this.close();
handler.onPress();
};
let size = button.size;
size.top = this.buttonHeight * (i + 1) + this.margin;
size.bottom = this.buttonHeight * (i + 2);
button.size = size;
button.hidden = false;
}
startAnimation()
{
this.lastTick = Date.now();
this.menuButtonPanel.onTick = this.onTick.bind(this);
}
/**
* Animate menu panel.
*/
onTick()
{
let tickLength = Date.now() - this.lastTick;
this.lastTick = Date.now();
let maxOffset =
this.endPosition + (
this.isOpen ?
-this.menuButtonPanel.size.bottom :
+this.menuButtonPanel.size.top);
if (maxOffset <= 0)
{
- // TODO: support actually deleting the handler
- this.menuButtonPanel.onTick = () => {};
+ delete this.menuButtonPanel.onTick;
return;
}
let offset = Math.min(this.Speed * tickLength, maxOffset) * (this.isOpen ? +1 : -1);
let size = this.menuButtonPanel.size;
size.top += offset;
size.bottom += offset;
this.menuButtonPanel.size = size;
}
}
/**
* Number of pixels per millisecond to move.
*/
Menu.prototype.Speed = 1.2;
Index: ps/trunk/source/gui/ObjectBases/IGUIObject.cpp
===================================================================
--- ps/trunk/source/gui/ObjectBases/IGUIObject.cpp (revision 23102)
+++ ps/trunk/source/gui/ObjectBases/IGUIObject.cpp (revision 23103)
@@ -1,502 +1,515 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "IGUIObject.h"
#include "gui/CGUI.h"
#include "gui/CGUISetting.h"
#include "ps/CLogger.h"
#include "ps/GameSetup/Config.h"
#include "ps/Profile.h"
#include "scriptinterface/ScriptInterface.h"
#include "soundmanager/ISoundManager.h"
IGUIObject::IGUIObject(CGUI& pGUI)
: m_pGUI(pGUI),
m_pParent(),
m_MouseHovering(),
m_LastClickTime(),
m_Enabled(),
m_Hidden(),
m_Size(),
m_Style(),
m_Hotkey(),
m_Z(),
m_Absolute(),
m_Ghost(),
m_AspectRatio(),
m_Tooltip(),
m_TooltipStyle()
{
RegisterSetting("enabled", m_Enabled);
RegisterSetting("hidden", m_Hidden);
RegisterSetting("size", m_Size);
RegisterSetting("style", m_Style);
RegisterSetting("hotkey", m_Hotkey);
RegisterSetting("z", m_Z);
RegisterSetting("absolute", m_Absolute);
RegisterSetting("ghost", m_Ghost);
RegisterSetting("aspectratio", m_AspectRatio);
RegisterSetting("tooltip", m_Tooltip);
RegisterSetting("tooltip_style", m_TooltipStyle);
// Setup important defaults
// TODO: Should be in the default style?
SetSetting("hidden", false, true);
SetSetting("ghost", false, true);
SetSetting("enabled", true, true);
SetSetting("absolute", true, true);
}
IGUIObject::~IGUIObject()
{
for (const std::pair& p : m_Settings)
delete p.second;
if (!m_ScriptHandlers.empty())
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this);
// m_Children is deleted along all other GUI Objects in the CGUI destructor
}
void IGUIObject::AddChild(IGUIObject& pChild)
{
pChild.SetParent(this);
m_Children.push_back(&pChild);
}
template
void IGUIObject::RegisterSetting(const CStr& Name, T& Value)
{
if (SettingExists(Name))
LOGERROR("The setting '%s' already exists on the object '%s'!", Name.c_str(), GetPresentableName().c_str());
else
m_Settings.emplace(Name, new CGUISetting(*this, Name, Value));
}
bool IGUIObject::SettingExists(const CStr& Setting) const
{
return m_Settings.find(Setting) != m_Settings.end();
}
template
T& IGUIObject::GetSetting(const CStr& Setting)
{
return static_cast* >(m_Settings.at(Setting))->m_pSetting;
}
template
const T& IGUIObject::GetSetting(const CStr& Setting) const
{
return static_cast* >(m_Settings.at(Setting))->m_pSetting;
}
bool IGUIObject::SetSettingFromString(const CStr& Setting, const CStrW& Value, const bool SendMessage)
{
const std::map::iterator it = m_Settings.find(Setting);
if (it == m_Settings.end())
{
LOGERROR("GUI object '%s' has no property called '%s', can't set parse and set value '%s'", GetPresentableName().c_str(), Setting.c_str(), Value.ToUTF8().c_str());
return false;
}
return it->second->FromString(Value, SendMessage);
}
template
void IGUIObject::SetSetting(const CStr& Setting, T& Value, const bool SendMessage)
{
PreSettingChange(Setting);
static_cast* >(m_Settings.at(Setting))->m_pSetting = std::move(Value);
SettingChanged(Setting, SendMessage);
}
template
void IGUIObject::SetSetting(const CStr& Setting, const T& Value, const bool SendMessage)
{
PreSettingChange(Setting);
static_cast* >(m_Settings.at(Setting))->m_pSetting = Value;
SettingChanged(Setting, SendMessage);
}
void IGUIObject::PreSettingChange(const CStr& Setting)
{
if (Setting == "hotkey")
m_pGUI.UnsetObjectHotkey(this, GetSetting(Setting));
}
void IGUIObject::SettingChanged(const CStr& Setting, const bool SendMessage)
{
if (Setting == "size")
{
// If setting was "size", we need to re-cache itself and all children
RecurseObject(nullptr, &IGUIObject::UpdateCachedSize);
}
else if (Setting == "hidden")
{
// Hiding an object requires us to reset it and all children
if (m_Hidden)
RecurseObject(nullptr, &IGUIObject::ResetStates);
}
else if (Setting == "hotkey")
m_pGUI.SetObjectHotkey(this, GetSetting(Setting));
if (SendMessage)
{
SGUIMessage msg(GUIM_SETTINGS_UPDATED, Setting);
HandleMessage(msg);
}
}
bool IGUIObject::IsMouseOver() const
{
return m_CachedActualSize.PointInside(m_pGUI.GetMousePos());
}
bool IGUIObject::MouseOverIcon()
{
return false;
}
void IGUIObject::UpdateMouseOver(IGUIObject* const& pMouseOver)
{
if (pMouseOver == this)
{
if (!m_MouseHovering)
SendEvent(GUIM_MOUSE_ENTER, "mouseenter");
m_MouseHovering = true;
SendEvent(GUIM_MOUSE_OVER, "mousemove");
}
else
{
if (m_MouseHovering)
{
m_MouseHovering = false;
SendEvent(GUIM_MOUSE_LEAVE, "mouseleave");
}
}
}
void IGUIObject::ChooseMouseOverAndClosest(IGUIObject*& pObject)
{
if (!IsMouseOver())
return;
// Check if we've got competition at all
if (pObject == nullptr)
{
pObject = this;
return;
}
// Or if it's closer
if (GetBufferedZ() >= pObject->GetBufferedZ())
{
pObject = this;
return;
}
}
IGUIObject* IGUIObject::GetParent() const
{
// Important, we're not using GetParent() for these
// checks, that could screw it up
if (m_pParent && m_pParent->m_pParent == nullptr)
return nullptr;
return m_pParent;
}
void IGUIObject::ResetStates()
{
// Notify the gui that we aren't hovered anymore
UpdateMouseOver(nullptr);
}
void IGUIObject::UpdateCachedSize()
{
// If absolute="false" and the object has got a parent,
// use its cached size instead of the screen. Notice
// it must have just been cached for it to work.
if (!m_Absolute && m_pParent && !IsRootObject())
m_CachedActualSize = m_Size.GetSize(m_pParent->m_CachedActualSize);
else
m_CachedActualSize = m_Size.GetSize(CRect(0.f, 0.f, g_xres / g_GuiScale, g_yres / g_GuiScale));
// In a few cases, GUI objects have to resize to fill the screen
// but maintain a constant aspect ratio.
// Adjust the size to be the max possible, centered in the original size:
if (m_AspectRatio)
{
if (m_CachedActualSize.GetWidth() > m_CachedActualSize.GetHeight() * m_AspectRatio)
{
float delta = m_CachedActualSize.GetWidth() - m_CachedActualSize.GetHeight() * m_AspectRatio;
m_CachedActualSize.left += delta/2.f;
m_CachedActualSize.right -= delta/2.f;
}
else
{
float delta = m_CachedActualSize.GetHeight() - m_CachedActualSize.GetWidth() / m_AspectRatio;
m_CachedActualSize.bottom -= delta/2.f;
m_CachedActualSize.top += delta/2.f;
}
}
}
void IGUIObject::LoadStyle(const CStr& StyleName)
{
if (!m_pGUI.HasStyle(StyleName))
debug_warn(L"IGUIObject::LoadStyle failed");
// The default style may specify settings for any GUI object.
// Other styles are reported if they specify a Setting that does not exist,
// so that the XML author is informed and can correct the style.
for (const std::pair& p : m_pGUI.GetStyle(StyleName).m_SettingsDefaults)
{
if (SettingExists(p.first))
SetSettingFromString(p.first, p.second, true);
else if (StyleName != "default")
LOGWARNING("GUI object has no setting \"%s\", but the style \"%s\" defines it", p.first, StyleName.c_str());
}
}
float IGUIObject::GetBufferedZ() const
{
if (m_Absolute)
return m_Z;
if (GetParent())
return GetParent()->GetBufferedZ() + m_Z;
// In philosophy, a parentless object shouldn't be able to have a relative sizing,
// but we'll accept it so that absolute can be used as default without a complaint.
// Also, you could consider those objects children to the screen resolution.
return m_Z;
}
void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI& pGUI)
{
JSContext* cx = pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue globalVal(cx, pGUI.GetGlobalObject());
JS::RootedObject globalObj(cx, &globalVal.toObject());
const int paramCount = 1;
const char* paramNames[paramCount] = { "mouse" };
// Location to report errors from
CStr CodeName = GetName()+" "+Action;
// Generate a unique name
static int x = 0;
char buf[64];
sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, Action.c_str());
JS::CompileOptions options(cx);
options.setFileAndLine(CodeName.c_str(), 0);
options.setIsRunOnce(false);
JS::RootedFunction func(cx);
JS::AutoObjectVector emptyScopeChain(cx);
if (!JS::CompileFunction(cx, emptyScopeChain, options, buf, paramCount, paramNames, Code.c_str(), Code.length(), &func))
{
LOGERROR("RegisterScriptHandler: Failed to compile the script for %s", Action.c_str());
return;
}
JS::RootedObject funcObj(cx, JS_GetFunctionObject(func));
SetScriptHandler(Action, funcObj);
}
void IGUIObject::SetScriptHandler(const CStr& Action, JS::HandleObject Function)
{
if (m_ScriptHandlers.empty())
JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this);
m_ScriptHandlers[Action] = JS::Heap(Function);
}
+void IGUIObject::UnsetScriptHandler(const CStr& Action)
+{
+ std::map >::iterator it = m_ScriptHandlers.find(Action);
+
+ if (it == m_ScriptHandlers.end())
+ return;
+
+ m_ScriptHandlers.erase(it);
+
+ if (m_ScriptHandlers.empty())
+ JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this);
+}
+
InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& EventName)
{
PROFILE2_EVENT("gui event");
PROFILE2_ATTR("type: %s", EventName.c_str());
PROFILE2_ATTR("object: %s", m_Name.c_str());
SGUIMessage msg(type);
HandleMessage(msg);
ScriptEvent(EventName);
return (msg.skipped ? IN_PASS : IN_HANDLED);
}
void IGUIObject::ScriptEvent(const CStr& Action)
{
std::map >::iterator it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
JSContext* cx = m_pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
// Set up the 'mouse' parameter
JS::RootedValue mouse(cx);
const CPos& mousePos = m_pGUI.GetMousePos();
ScriptInterface::CreateObject(
cx,
&mouse,
"x", mousePos.x,
"y", mousePos.y,
"buttons", m_pGUI.GetMouseButtons());
JS::AutoValueVector paramData(cx);
paramData.append(mouse);
JS::RootedObject obj(cx, GetJSObject());
JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second));
JS::RootedValue result(cx);
bool ok = JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result);
if (!ok)
{
// We have no way to propagate the script exception, so just ignore it
// and hope the caller checks JS_IsExceptionPending
}
}
void IGUIObject::ScriptEvent(const CStr& Action, const JS::HandleValueArray& paramData)
{
std::map >::iterator it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
JSContext* cx = m_pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, GetJSObject());
JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second));
JS::RootedValue result(cx);
if (!JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result))
JS_ReportError(cx, "Errors executing script action \"%s\"", Action.c_str());
}
void IGUIObject::CreateJSObject()
{
JSContext* cx = m_pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
m_JSObject.init(cx, m_pGUI.GetScriptInterface()->CreateCustomObject("GUIObject"));
JS_SetPrivate(m_JSObject.get(), this);
RegisterScriptFunctions();
}
JSObject* IGUIObject::GetJSObject()
{
// Cache the object when somebody first asks for it, because otherwise
// we end up doing far too much object allocation.
if (!m_JSObject.initialized())
CreateJSObject();
return m_JSObject.get();
}
bool IGUIObject::IsEnabled() const
{
return m_Enabled;
}
bool IGUIObject::IsHidden() const
{
return m_Hidden;
}
bool IGUIObject::IsHiddenOrGhost() const
{
return m_Hidden || m_Ghost;
}
void IGUIObject::PlaySound(const CStrW& soundPath) const
{
if (g_SoundManager && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
CStr IGUIObject::GetPresentableName() const
{
// __internal(), must be at least 13 letters to be able to be
// an internal name
if (m_Name.length() <= 12)
return m_Name;
if (m_Name.substr(0, 10) == "__internal")
return CStr("[unnamed object]");
else
return m_Name;
}
void IGUIObject::SetFocus()
{
m_pGUI.SetFocusedObject(this);
}
bool IGUIObject::IsFocused() const
{
return m_pGUI.GetFocusedObject() == this;
}
bool IGUIObject::IsBaseObject() const
{
return this == &m_pGUI.GetBaseObject();
}
bool IGUIObject::IsRootObject() const
{
return m_pParent == &m_pGUI.GetBaseObject();
}
void IGUIObject::TraceMember(JSTracer* trc)
{
// Please ensure to adapt the Tracer enabling and disabling in accordance with the GC things traced!
for (std::pair>& handler : m_ScriptHandlers)
JS_CallObjectTracer(trc, &handler.second, "IGUIObject::m_ScriptHandlers");
}
// Instantiate templated functions:
// These functions avoid copies by working with a reference and move semantics.
#define TYPE(T) \
template void IGUIObject::RegisterSetting(const CStr& Name, T& Value); \
template T& IGUIObject::GetSetting(const CStr& Setting); \
template const T& IGUIObject::GetSetting(const CStr& Setting) const; \
template void IGUIObject::SetSetting(const CStr& Setting, T& Value, const bool SendMessage); \
#include "gui/GUISettingTypes.h"
#undef TYPE
// Copying functions - discouraged except for primitives.
#define TYPE(T) \
template void IGUIObject::SetSetting(const CStr& Setting, const T& Value, const bool SendMessage); \
#define GUITYPE_IGNORE_NONCOPYABLE
#include "gui/GUISettingTypes.h"
#undef GUITYPE_IGNORE_NONCOPYABLE
#undef TYPE
Index: ps/trunk/source/gui/ObjectBases/IGUIObject.h
===================================================================
--- ps/trunk/source/gui/ObjectBases/IGUIObject.h (revision 23102)
+++ ps/trunk/source/gui/ObjectBases/IGUIObject.h (revision 23103)
@@ -1,496 +1,505 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* The base class of an object.
* All objects are derived from this class.
* It's an abstract data type, so it can't be used per se.
* Also contains a Dummy object which is used for completely blank objects.
*/
#ifndef INCLUDED_IGUIOBJECT
#define INCLUDED_IGUIOBJECT
#include "gui/Scripting/JSInterface_IGUIObject.h"
#include "gui/SettingTypes/CGUISize.h"
#include "gui/SGUIMessage.h"
#include "lib/input.h" // just for IN_PASS
#include "ps/XML/Xeromyces.h"
#include