Browse Source

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 years ago
parent
commit
96855411c0

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

@@ -352,7 +352,9 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (key == KEY_ESC)
     {
         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();
         else if (front is editorSettingsDialog || front is sceneWindow || front is nodeWindow)
             front.visible = false;

+ 1 - 1
Engine/UI/LineEdit.cpp

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

+ 25 - 2
Engine/UI/ListView.cpp

@@ -67,6 +67,7 @@ ListView::ListView(Context* context) :
     SetContentElement(container);
     
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
+    SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleFocusChanged));
 }
 
 ListView::~ListView()
@@ -662,7 +663,7 @@ void ListView::UpdateSelectionEffect()
     {
         UIElement* item = GetItem(i);
         if (highlightMode_ != HM_NEVER && selections_.Find(i) != selections_.End())
-            item->SetSelected(focus_ || highlightMode_ == HM_ALWAYS);
+            item->SetSelected(HasFocus() || highlightMode_ == HM_ALWAYS);
         else
             item->SetSelected(false);
     }
@@ -670,7 +671,11 @@ void ListView::UpdateSelectionEffect()
 
 void ListView::EnsureItemVisibility(unsigned index)
 {
-    UIElement* item = GetItem(index);
+    EnsureItemVisibility(GetItem(index));
+}
+
+void ListView::EnsureItemVisibility(UIElement* item)
+{
     if (!item || !item->IsVisible())
         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();
     /// Ensure full visibility of the item.
     void EnsureItemVisibility(unsigned index);
+    /// Ensure full visibility of the item.
+    void EnsureItemVisibility(UIElement* item);
     
     /// Current selection.
     Set<unsigned> selections_;
@@ -157,4 +159,6 @@ protected:
 private:
     /// Handle global UI mouseclick to check for selection change.
     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)
 {
-    if (!dragSlider_)
+    if (!dragSlider_ || GetSize() == knob_->GetSize())
         return;
     
     float newValue = value_;
@@ -113,14 +113,14 @@ void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
     if (orientation_ == O_HORIZONTAL)
     {
         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
     {
         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);
@@ -176,24 +176,28 @@ void Slider::ChangeValue(float delta)
 
 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
     {
-        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)
     {
         // Return if already has focus
-        if (element->HasFocus())
+        if (focusElement_ == element)
             return;
+        
         // If element can not be focused, and does not reset the focus either, search toward the parent
         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)
-        element->SetFocus(true);
+    {
+        focusElement_ = element;
+        element->OnFocus();
+        
+        VariantMap focusEventData;
+        focusEventData[Focused::P_ELEMENT] = element;
+        element->SendEvent(E_FOCUSED, focusEventData);
+    }
     
     eventData[P_ELEMENT] = (void*)element;
     SendEvent(E_FOCUSCHANGED, eventData);
@@ -207,15 +218,6 @@ void UI::Update(float timeStep)
             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_);
 }
 
@@ -367,17 +369,7 @@ UIElement* UI::GetElementAt(int x, int y, bool activeOnly)
 
 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
@@ -671,8 +663,8 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
         }
         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;
@@ -790,7 +782,7 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
         }
         // Defocus the element
         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
         else
             element->OnKey(key, mouseButtons_, qualifiers_);

+ 2 - 2
Engine/UI/UI.h

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

+ 22 - 14
Engine/UI/UIElement.cpp

@@ -25,6 +25,7 @@
 #include "Context.h"
 #include "ResourceCache.h"
 #include "StringUtils.h"
+#include "UI.h"
 #include "UIElement.h"
 #include "UIEvents.h"
 
@@ -77,7 +78,6 @@ UIElement::UIElement(Context* context) :
     clipChildren_(false),
     active_(false),
     focusMode_(FM_NOTFOCUSABLE),
-    focus_(false),
     selected_(false),
     visible_(true),
     hovering_(false),
@@ -526,20 +526,19 @@ void UIElement::SetFocus(bool enable)
     if (focusMode_ < FM_FOCUSABLE)
         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_;
 }
 
+bool UIElement::HasFocus() const
+{
+    UI* ui = GetSubsystem<UI>();
+    if (!ui)
+        return false;
+    else
+        return ui->GetFocusElement() == this;
+}
+
 PODVector<UIElement*> UIElement::GetChildren(bool recursive) const
 {
     if (!recursive)

+ 2 - 4
Engine/UI/UIElement.h

@@ -207,7 +207,7 @@ public:
     void SetClipChildren(bool enable);
     /// %Set whether reacts to input.
     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);
     /// %Set selected mode. Actual meaning is element dependent, for example constant hover or pressed effect.
     void SetSelected(bool enable);
@@ -297,7 +297,7 @@ public:
     /// Return whether should clip child elements.
     bool GetClipChildren() const { return clipChildren_; }
     /// Return whether has focus.
-    bool HasFocus() const { return focus_; }
+    bool HasFocus() const;
     /// Return whether reacts to input.
     bool IsActive() const { return active_; }
     /// Return whether is selected. Actual meaning is element dependent.
@@ -381,8 +381,6 @@ protected:
     bool bringToBack_;
     /// Clip children flag.
     bool clipChildren_;
-    /// Focused flag.
-    bool focus_;
     /// Input enabled flag.
     bool active_;
     /// Selected flag.