Răsfoiți Sursa

Fixed unnecessarily complicated UI element focusing/defocusing logic.
Fixed slider bar size and positioning logic.
Ensure ListView item visibility also when pressing TAB to cycle focus.
Hide console if ESC pressed in the editor.

Lasse Öörni 14 ani în urmă
părinte
comite
96855411c0

+ 3 - 1
Bin/Data/Scripts/Editor/EditorUI.as

@@ -352,7 +352,9 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (key == KEY_ESC)
     if (key == KEY_ESC)
     {
     {
         UIElement@ front = ui.frontElement;
         UIElement@ front = ui.frontElement;
-        if (uiFileSelector !is null && front is uiFileSelector.window)
+        if (console.visible)
+            console.visible = false;
+        else if (uiFileSelector !is null && front is uiFileSelector.window)
             CloseFileSelector();
             CloseFileSelector();
         else if (front is editorSettingsDialog || front is sceneWindow || front is nodeWindow)
         else if (front is editorSettingsDialog || front is sceneWindow || front is nodeWindow)
             front.visible = false;
             front.visible = false;

+ 1 - 1
Engine/UI/LineEdit.cpp

@@ -123,7 +123,7 @@ void LineEdit::Update(float timeStep)
     }
     }
    
    
     bool cursorVisible = false;
     bool cursorVisible = false;
-    if (focus_)
+    if (HasFocus())
         cursorVisible = cursorBlinkTimer_ < 0.5f;
         cursorVisible = cursorBlinkTimer_ < 0.5f;
     cursor_->SetVisible(cursorVisible);
     cursor_->SetVisible(cursorVisible);
 }
 }

+ 25 - 2
Engine/UI/ListView.cpp

@@ -67,6 +67,7 @@ ListView::ListView(Context* context) :
     SetContentElement(container);
     SetContentElement(container);
     
     
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
+    SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleFocusChanged));
 }
 }
 
 
 ListView::~ListView()
 ListView::~ListView()
@@ -662,7 +663,7 @@ void ListView::UpdateSelectionEffect()
     {
     {
         UIElement* item = GetItem(i);
         UIElement* item = GetItem(i);
         if (highlightMode_ != HM_NEVER && selections_.Find(i) != selections_.End())
         if (highlightMode_ != HM_NEVER && selections_.Find(i) != selections_.End())
-            item->SetSelected(focus_ || highlightMode_ == HM_ALWAYS);
+            item->SetSelected(HasFocus() || highlightMode_ == HM_ALWAYS);
         else
         else
             item->SetSelected(false);
             item->SetSelected(false);
     }
     }
@@ -670,7 +671,11 @@ void ListView::UpdateSelectionEffect()
 
 
 void ListView::EnsureItemVisibility(unsigned index)
 void ListView::EnsureItemVisibility(unsigned index)
 {
 {
-    UIElement* item = GetItem(index);
+    EnsureItemVisibility(GetItem(index));
+}
+
+void ListView::EnsureItemVisibility(UIElement* item)
+{
     if (!item || !item->IsVisible())
     if (!item || !item->IsVisible())
         return;
         return;
     
     
@@ -777,3 +782,21 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
         }
         }
     }
     }
 }
 }
+
+void ListView::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
+{
+    using namespace FocusChanged;
+    
+    UIElement* element = static_cast<UIElement*>(eventData[P_ELEMENT].GetPtr());
+    while (element)
+    {
+        // If the focused element or its parent is in the list, scroll the list to make the item visible
+        UIElement* parent = element->GetParent();
+        if (parent == contentElement_)
+        {
+            EnsureItemVisibility(element);
+            return;
+        }
+        element = parent;
+    }
+}

+ 4 - 0
Engine/UI/ListView.h

@@ -136,6 +136,8 @@ protected:
     void UpdateSelectionEffect();
     void UpdateSelectionEffect();
     /// Ensure full visibility of the item.
     /// Ensure full visibility of the item.
     void EnsureItemVisibility(unsigned index);
     void EnsureItemVisibility(unsigned index);
+    /// Ensure full visibility of the item.
+    void EnsureItemVisibility(UIElement* item);
     
     
     /// Current selection.
     /// Current selection.
     Set<unsigned> selections_;
     Set<unsigned> selections_;
@@ -157,4 +159,6 @@ protected:
 private:
 private:
     /// Handle global UI mouseclick to check for selection change.
     /// Handle global UI mouseclick to check for selection change.
     void HandleUIMouseClick(StringHash eventType, VariantMap& eventData);
     void HandleUIMouseClick(StringHash eventType, VariantMap& eventData);
+    /// Handle focus change to check whether an invisible item was focused.
+    void HandleFocusChanged(StringHash eventType, VariantMap& eventData);
 };
 };

+ 24 - 20
Engine/UI/Slider.cpp

@@ -104,7 +104,7 @@ void Slider::OnDragStart(const IntVector2& position, const IntVector2& screenPos
 
 
 void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
-    if (!dragSlider_)
+    if (!dragSlider_ || GetSize() == knob_->GetSize())
         return;
         return;
     
     
     float newValue = value_;
     float newValue = value_;
@@ -113,14 +113,14 @@ void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
     if (orientation_ == O_HORIZONTAL)
     if (orientation_ == O_HORIZONTAL)
     {
     {
         int newX = Clamp(dragStartPosition_.x_ + delta.x_, 0, GetWidth() - knob_->GetWidth());
         int newX = Clamp(dragStartPosition_.x_ + delta.x_, 0, GetWidth() - knob_->GetWidth());
-        knob_->SetPosition(newX, dragStartPosition_.y_);
-        newValue = Clamp((float)newX * (range_ + 1.0f) / (float)GetWidth(), 0.0f, range_);
+        knob_->SetPosition(newX, 0);
+        newValue = (float)newX * range_ / (float)(GetWidth() - knob_->GetWidth());
     }
     }
     else
     else
     {
     {
         int newY = Clamp(dragStartPosition_.y_ + delta.y_, 0, GetHeight() - knob_->GetHeight());
         int newY = Clamp(dragStartPosition_.y_ + delta.y_, 0, GetHeight() - knob_->GetHeight());
-        knob_->SetPosition(dragStartPosition_.x_, newY);
-        newValue = Clamp((float)newY * (range_ + 1.0f) / (float)GetHeight(), 0.0f, range_);
+        knob_->SetPosition(0, newY);
+        newValue = (float)newY * range_ / (float)(GetHeight() - knob_->GetHeight());
     }
     }
     
     
     SetValue(newValue);
     SetValue(newValue);
@@ -176,24 +176,28 @@ void Slider::ChangeValue(float delta)
 
 
 void Slider::UpdateSlider()
 void Slider::UpdateSlider()
 {
 {
-    if (orientation_ == O_HORIZONTAL)
+    const IntRect& border = knob_->GetBorder();
+    
+    if (range_ > 0.0f)
     {
     {
-        float width = (float)GetWidth();
-        if (width < M_EPSILON)
-            return;
-        float sliderLength = width / (range_ + 1.0f);
-        float sliderPos = width * value_ / (range_ + 1.0f);
-        knob_->SetSize((int)sliderLength, GetHeight());
-        knob_->SetPosition(Clamp((int)(sliderPos + 0.5f), 0, GetWidth() - knob_->GetWidth()), 0);
+        if (orientation_ == O_HORIZONTAL)
+        {
+            int sliderLength = (int)Max((float)GetWidth() / (range_ + 1.0f), (float)(border.left_ + border.right_));
+            float sliderPos = (float)(GetWidth() - sliderLength) * value_ / range_;
+            knob_->SetSize(sliderLength, GetHeight());
+            knob_->SetPosition(Clamp((int)(sliderPos + 0.5f), 0, GetWidth() - knob_->GetWidth()), 0);
+        }
+        else
+        {
+            int sliderLength = (int)Max((float)GetHeight() / (range_ + 1.0f), (float)(border.top_ + border.bottom_));
+            float sliderPos = (float)(GetHeight() - sliderLength) * value_ / range_;
+            knob_->SetSize(GetWidth(), sliderLength);
+            knob_->SetPosition(0, Clamp((int)(sliderPos + 0.5f), 0, GetHeight() - knob_->GetHeight()));
+        }
     }
     }
     else
     else
     {
     {
-        float height = (float)GetHeight();
-        if (height < M_EPSILON)
-            return;
-        float sliderLength = height / (range_ + 1.0f);
-        float sliderPos = height * value_ / (range_ + 1.0f);
-        knob_->SetSize(GetWidth(), (int)sliderLength);
-        knob_->SetPosition(0, Clamp((int)(sliderPos + 0.5f), 0, GetHeight() - knob_->GetHeight()));
+        knob_->SetSize(GetSize());
+        knob_->SetPosition(0, 0);
     }
     }
 }
 }

+ 24 - 32
Engine/UI/UI.cpp

@@ -120,8 +120,9 @@ void UI::SetFocusElement(UIElement* element)
     if (element)
     if (element)
     {
     {
         // Return if already has focus
         // Return if already has focus
-        if (element->HasFocus())
+        if (focusElement_ == element)
             return;
             return;
+        
         // If element can not be focused, and does not reset the focus either, search toward the parent
         // If element can not be focused, and does not reset the focus either, search toward the parent
         for (;;)
         for (;;)
         {
         {
@@ -134,18 +135,28 @@ void UI::SetFocusElement(UIElement* element)
         }
         }
     }
     }
     
     
-    PODVector<UIElement*> allChildren = rootElement_->GetChildren(true);
-    
-    // Go through all elements to clear the old focus
-    for (PODVector<UIElement*>::Iterator i = allChildren.Begin(); i != allChildren.End(); ++i)
+    // Remove focus from the old element
+    if (focusElement_)
     {
     {
-        UIElement* other = *i;
-        if (other != element && other->HasFocus())
-            other->SetFocus(false);
+        UIElement* oldFocusElement = focusElement_;
+        focusElement_.Reset();
+        oldFocusElement->OnDefocus();
+        
+        VariantMap focusEventData;
+        focusEventData[Defocused::P_ELEMENT] = oldFocusElement;
+        oldFocusElement->SendEvent(E_DEFOCUSED, focusEventData);
     }
     }
     
     
+    // Then set focus to the new
     if (element)
     if (element)
-        element->SetFocus(true);
+    {
+        focusElement_ = element;
+        element->OnFocus();
+        
+        VariantMap focusEventData;
+        focusEventData[Focused::P_ELEMENT] = element;
+        element->SendEvent(E_FOCUSED, focusEventData);
+    }
     
     
     eventData[P_ELEMENT] = (void*)element;
     eventData[P_ELEMENT] = (void*)element;
     SendEvent(E_FOCUSCHANGED, eventData);
     SendEvent(E_FOCUSCHANGED, eventData);
@@ -207,15 +218,6 @@ void UI::Update(float timeStep)
             cursor_->SetShape(dragElement_ == element ? CS_ACCEPTDROP : CS_REJECTDROP);
             cursor_->SetShape(dragElement_ == element ? CS_ACCEPTDROP : CS_REJECTDROP);
     }
     }
     
     
-    // Defocus element now if should
-    if (defocusElement_)
-    {
-        // Do nothing if the focus element changed in the meanwhile
-        if (defocusElement_ == GetFocusElement())
-            SetFocusElement(0);
-        defocusElement_.Reset();
-    }
-    
     Update(timeStep, rootElement_);
     Update(timeStep, rootElement_);
 }
 }
 
 
@@ -367,17 +369,7 @@ UIElement* UI::GetElementAt(int x, int y, bool activeOnly)
 
 
 UIElement* UI::GetFocusElement() const
 UIElement* UI::GetFocusElement() const
 {
 {
-    if (!rootElement_)
-        return 0;
-    
-    PODVector<UIElement*> allChildren = rootElement_->GetChildren(true);
-    for (PODVector<UIElement*>::Iterator i = allChildren.Begin(); i != allChildren.End(); ++i)
-    {
-        if ((*i)->HasFocus())
-            return *i;
-    }
-    
-    return 0;
+    return focusElement_;
 }
 }
 
 
 UIElement* UI::GetFrontElement() const
 UIElement* UI::GetFrontElement() const
@@ -671,8 +663,8 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
         }
         }
         else
         else
         {
         {
-            // If clicked over no element, or a disabled element, try to lose focus
-            defocusElement_ = GetFocusElement();
+            // If clicked over no element, or a disabled element, lose focus
+            SetFocusElement(0);
         }
         }
         
         
         using namespace UIMouseClick;
         using namespace UIMouseClick;
@@ -790,7 +782,7 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
         }
         }
         // Defocus the element
         // Defocus the element
         else if (key == KEY_ESC && element->GetFocusMode() == FM_FOCUSABLE_DEFOCUSABLE)
         else if (key == KEY_ESC && element->GetFocusMode() == FM_FOCUSABLE_DEFOCUSABLE)
-            defocusElement_ = element;
+            element->SetFocus(false);
         // If none of the special keys, pass the key to the focused element
         // If none of the special keys, pass the key to the focused element
         else
         else
             element->OnKey(key, mouseButtons_, qualifiers_);
             element->OnKey(key, mouseButtons_, qualifiers_);

+ 2 - 2
Engine/UI/UI.h

@@ -131,8 +131,8 @@ private:
     SharedPtr<Cursor> cursor_;
     SharedPtr<Cursor> cursor_;
     /// UI element being dragged.
     /// UI element being dragged.
     WeakPtr<UIElement> dragElement_;
     WeakPtr<UIElement> dragElement_;
-    /// Element to defocus on the next update.
-    WeakPtr<UIElement> defocusElement_;
+    /// Currently focused element
+    WeakPtr<UIElement> focusElement_;
     /// UI rendering batches.
     /// UI rendering batches.
     PODVector<UIBatch> batches_;
     PODVector<UIBatch> batches_;
     /// UI rendering quads.
     /// UI rendering quads.

+ 22 - 14
Engine/UI/UIElement.cpp

@@ -25,6 +25,7 @@
 #include "Context.h"
 #include "Context.h"
 #include "ResourceCache.h"
 #include "ResourceCache.h"
 #include "StringUtils.h"
 #include "StringUtils.h"
+#include "UI.h"
 #include "UIElement.h"
 #include "UIElement.h"
 #include "UIEvents.h"
 #include "UIEvents.h"
 
 
@@ -77,7 +78,6 @@ UIElement::UIElement(Context* context) :
     clipChildren_(false),
     clipChildren_(false),
     active_(false),
     active_(false),
     focusMode_(FM_NOTFOCUSABLE),
     focusMode_(FM_NOTFOCUSABLE),
-    focus_(false),
     selected_(false),
     selected_(false),
     visible_(true),
     visible_(true),
     hovering_(false),
     hovering_(false),
@@ -526,20 +526,19 @@ void UIElement::SetFocus(bool enable)
     if (focusMode_ < FM_FOCUSABLE)
     if (focusMode_ < FM_FOCUSABLE)
         enable = false;
         enable = false;
     
     
-    if (enable != focus_)
+    UI* ui = GetSubsystem<UI>();
+    if (!ui)
+        return;
+    
+    if (enable)
     {
     {
-        focus_ = enable;
-        
-        if (enable)
-            OnFocus();
-        else
-            OnDefocus();
-        
-        using namespace Focused;
-        
-        VariantMap eventData;
-        eventData[P_ELEMENT] = (void*)this;
-        SendEvent(focus_ ? E_FOCUSED : E_DEFOCUSED, eventData);
+        if (ui->GetFocusElement() != this)
+            ui->SetFocusElement(this);
+    }
+    else
+    {
+        if (ui->GetFocusElement() == this)
+            ui->SetFocusElement(0);
     }
     }
 }
 }
 
 
@@ -906,6 +905,15 @@ float UIElement::GetDerivedOpacity()
     return derivedOpacity_;
     return derivedOpacity_;
 }
 }
 
 
+bool UIElement::HasFocus() const
+{
+    UI* ui = GetSubsystem<UI>();
+    if (!ui)
+        return false;
+    else
+        return ui->GetFocusElement() == this;
+}
+
 PODVector<UIElement*> UIElement::GetChildren(bool recursive) const
 PODVector<UIElement*> UIElement::GetChildren(bool recursive) const
 {
 {
     if (!recursive)
     if (!recursive)

+ 2 - 4
Engine/UI/UIElement.h

@@ -207,7 +207,7 @@ public:
     void SetClipChildren(bool enable);
     void SetClipChildren(bool enable);
     /// %Set whether reacts to input.
     /// %Set whether reacts to input.
     void SetActive(bool enable);
     void SetActive(bool enable);
-    /// %Set whether is focused. Usually called by UI. If called manually, other elements should be defocused.
+    /// %Set whether is focused. Only one element can be focused at a time.
     void SetFocus(bool enable);
     void SetFocus(bool enable);
     /// %Set selected mode. Actual meaning is element dependent, for example constant hover or pressed effect.
     /// %Set selected mode. Actual meaning is element dependent, for example constant hover or pressed effect.
     void SetSelected(bool enable);
     void SetSelected(bool enable);
@@ -297,7 +297,7 @@ public:
     /// Return whether should clip child elements.
     /// Return whether should clip child elements.
     bool GetClipChildren() const { return clipChildren_; }
     bool GetClipChildren() const { return clipChildren_; }
     /// Return whether has focus.
     /// Return whether has focus.
-    bool HasFocus() const { return focus_; }
+    bool HasFocus() const;
     /// Return whether reacts to input.
     /// Return whether reacts to input.
     bool IsActive() const { return active_; }
     bool IsActive() const { return active_; }
     /// Return whether is selected. Actual meaning is element dependent.
     /// Return whether is selected. Actual meaning is element dependent.
@@ -381,8 +381,6 @@ protected:
     bool bringToBack_;
     bool bringToBack_;
     /// Clip children flag.
     /// Clip children flag.
     bool clipChildren_;
     bool clipChildren_;
-    /// Focused flag.
-    bool focus_;
     /// Input enabled flag.
     /// Input enabled flag.
     bool active_;
     bool active_;
     /// Selected flag.
     /// Selected flag.