Browse Source

Make MessageBox a UIElement subclass for automatic lifetime management through the UI hierarchy. (Use MessageBox:new in Lua for safe operation.) Remove the "test all factories" feature in Engine class (Debug mode) since it would leave a MessageBox in the UI. Use UI root size for centering the MessageBox instead of Graphics resolution, since the UI might be scaled. Closes #1439. Closes #634.

Lasse Öörni 9 years ago
parent
commit
c38d29a20c

+ 6 - 3
Source/Urho3D/AngelScript/APITemplates.h

@@ -1032,11 +1032,14 @@ static XMLFile* UIElementGetDefaultStyle(UIElement* ptr)
 #endif
 
 /// Template function for registering a class derived from UIElement.
-template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className, bool isSprite = false)
+template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className, bool isSprite = false, bool registerConstructor = true)
 {
     RegisterAnimatable<T>(engine, className);
-    RegisterObjectConstructor<T>(engine, className);
-    RegisterNamedObjectConstructor<T>(engine, className);
+    if (registerConstructor)
+    {
+        RegisterObjectConstructor<T>(engine, className);
+        RegisterNamedObjectConstructor<T>(engine, className);
+    }
     RegisterSubclass<UIElement, T>(engine, "UIElement", className);
     engine->RegisterObjectMethod(className, "bool LoadXML(const XMLElement&in, XMLFile@+, bool arg2 = false)", asMETHODPR(T, LoadXML, (const XMLElement&, XMLFile*, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool LoadXML(File@+)", asFUNCTIONPR(UIElementLoadXML, (File*, UIElement*), bool), asCALL_CDECL_OBJLAST);

+ 4 - 4
Source/Urho3D/AngelScript/UIAPI.cpp

@@ -496,15 +496,15 @@ static void RegisterMenu(asIScriptEngine* engine)
 static MessageBox* ConstructMessageBox(const String& messageString, const String& titleString, XMLFile* layoutFile, XMLFile* styleFile)
 {
     SharedPtr<MessageBox> messageBox(new MessageBox(GetScriptContext(), messageString, titleString, layoutFile, styleFile));
-    if (messageBox)
-        messageBox->AddRef();
+    messageBox->AddRef();
     return messageBox.Get();
 }
 
 static void RegisterMessageBox(asIScriptEngine* engine)
 {
-    RegisterObject<MessageBox>(engine, "MessageBox");
-    engine->RegisterObjectBehaviour("MessageBox", asBEHAVE_FACTORY, "MessageBox@+ f(const String&in messageString = String(), const String&in titleString = String(), XMLFile@+ layoutFile = null, XMLFile@+ styleFile = null)", asFUNCTION(ConstructMessageBox), asCALL_CDECL);
+    // Do not register default UIElement constructor due to possible ambiguity with the Messagebox parameter constructor
+    RegisterUIElement<MessageBox>(engine, "MessageBox", false, false);
+    engine->RegisterObjectBehaviour("MessageBox", asBEHAVE_FACTORY, "MessageBox@ f(const String&in messageString = String(), const String&in titleString = String(), XMLFile@+ layoutFile = null, XMLFile@+ styleFile = null)", asFUNCTION(ConstructMessageBox), asCALL_CDECL);
     engine->RegisterObjectMethod("MessageBox", "void set_title(const String&in)", asMETHOD(MessageBox, SetTitle), asCALL_THISCALL);
     engine->RegisterObjectMethod("MessageBox", "const String& get_title() const", asMETHOD(MessageBox, GetTitle), asCALL_THISCALL);
     engine->RegisterObjectMethod("MessageBox", "void set_message(const String&in)", asMETHOD(MessageBox, SetMessage), asCALL_THISCALL);

+ 0 - 9
Source/Urho3D/Engine/Engine.cpp

@@ -415,15 +415,6 @@ bool Engine::Initialize(const VariantMap& parameters)
         timeOut_ = GetParameter(parameters, "TimeOut", 0).GetInt() * 1000000LL;
 #endif
 
-    // In debug mode, check now that all factory created objects can be created without crashing
-#ifdef _DEBUG
-    if (!resourcePaths.Empty())
-    {
-        const HashMap<StringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
-        for (HashMap<StringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories.Begin(); i != factories.End(); ++i)
-            SharedPtr<Object> object = i->second_->CreateObject();
-    }
-#endif
 #ifdef URHO3D_PROFILING
     if (GetParameter(parameters, "EventProfiler", true).GetBool())
     {

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/UI/MessageBox.pkg

@@ -1,6 +1,6 @@
 $#include "UI/MessageBox.h"
 
-class MessageBox : public Object
+class MessageBox : public UIElement
 {
     MessageBox(const String messageString = String::EMPTY, const String titleString = String::EMPTY, XMLFile* layoutFile = 0, XMLFile* styleFile = 0);
     virtual ~MessageBox();

+ 27 - 14
Source/Urho3D/UI/MessageBox.cpp

@@ -39,7 +39,7 @@ namespace Urho3D
 
 MessageBox::MessageBox(Context* context, const String& messageString, const String& titleString, XMLFile* layoutFile,
     XMLFile* styleFile) :
-    Object(context),
+    UIElement(context),
     titleText_(0),
     messageText_(0),
     okButton_(0)
@@ -53,11 +53,16 @@ MessageBox::MessageBox(Context* context, const String& messageString, const Stri
             return;         // Note: windowless MessageBox should not be used!
     }
 
+    // MessageBox itself doesn't render anything. Add self to UI root for lifetime management
     UI* ui = GetSubsystem<UI>();
     window_ = ui->LoadLayout(layoutFile, styleFile);
     if (!window_)   // Error is already logged
         return;
-    ui->GetRoot()->AddChild(window_);
+
+    AddChild(window_);
+    UIElement* root = ui->GetRoot();
+    // Add self to UI hierarchy for lifetime management
+    root->AddChild(this);
 
     // Set the title and message strings if they are given
     titleText_ = dynamic_cast<Text*>(window_->GetChild("TitleText", true));
@@ -71,15 +76,8 @@ MessageBox::MessageBox(Context* context, const String& messageString, const Stri
     Window* window = dynamic_cast<Window*>(window_.Get());
     if (window)
     {
-        Graphics* graphics = GetSubsystem<Graphics>();  // May be null if headless
-        if (graphics)
-        {
-            const IntVector2& size = window->GetSize();
-            window->SetPosition((graphics->GetWidth() - size.x_) / 2, (graphics->GetHeight() - size.y_) / 2);
-        }
-        else
-            URHO3D_LOGWARNING("Instantiating a modal window in headless mode!");
-
+        const IntVector2& size = window->GetSize();
+        window->SetPosition((root->GetWidth() - size.x_) / 2, (root->GetHeight() - size.y_) / 2);
         window->SetModal(true);
         SubscribeToEvent(window, E_MODALCHANGED, URHO3D_HANDLER(MessageBox, HandleMessageAcknowledged));
     }
@@ -101,8 +99,7 @@ MessageBox::MessageBox(Context* context, const String& messageString, const Stri
 
 MessageBox::~MessageBox()
 {
-    if (window_)
-        window_->Remove();
+    RemoveWindow();
 }
 
 void MessageBox::RegisterObject(Context* context)
@@ -140,7 +137,23 @@ void MessageBox::HandleMessageAcknowledged(StringHash eventType, VariantMap& eve
     newEventData[P_OK] = eventData[Released::P_ELEMENT] == okButton_;
     SendEvent(E_MESSAGEACK, newEventData);
 
-    this->ReleaseRef();
+    // Remove the modal window now
+    RemoveWindow();
+
+    // Remove self from UI hierarchy. This should cause destruction of self in case no other strong refs exist
+    Remove();
+}
+
+void MessageBox::RemoveWindow()
+{
+    if (window_)
+    {
+        Window* window = dynamic_cast<Window*>(window_.Get());
+        if (window)
+            window->SetModal(false);
+        RemoveChild(window_);
+        window_.Reset();
+    }
 }
 
 }

+ 6 - 4
Source/Urho3D/UI/MessageBox.h

@@ -22,7 +22,7 @@
 
 #pragma once
 
-#include "../Core/Object.h"
+#include "../UI/UIElement.h"
 
 namespace Urho3D
 {
@@ -32,10 +32,10 @@ class Text;
 class UIElement;
 class XMLFile;
 
-/// Message box dialog.
-class URHO3D_API MessageBox : public Object
+/// Message box dialog. Manages its lifetime automatically using the UI hierarchy, so the application does not need to hold a reference to it, and shouldn't attempt to destroy it manually.
+class URHO3D_API MessageBox : public UIElement
 {
-    URHO3D_OBJECT(MessageBox, Object);
+    URHO3D_OBJECT(MessageBox, UIElement);
 
 public:
     /// Construct. If layout file is not given, use the default message box layout. If style file is not given, use the default style file from root UI element.
@@ -60,6 +60,8 @@ public:
     UIElement* GetWindow() const { return window_; }
 
 private:
+    /// Remove the dialog window from the UI hierarchy.
+    void RemoveWindow();
     /// Handle events that dismiss the message box.
     void HandleMessageAcknowledged(StringHash eventType, VariantMap& eventData);