Page 1 of 2

[PATCH] Inventory sort button

Posted: 04 Mar 2019, 15:32
by CyberShadow
Hello,

I made this patch for myself. Maybe someone else will find it useful it, too.

Screenshot:
Image

Code:

Code: Select all

diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp
index 1877ef97d..455a67cd8 100644
--- a/apps/openmw/mwgui/inventorywindow.cpp
+++ b/apps/openmw/mwgui/inventorywindow.cpp
@@ -745,7 +745,7 @@ namespace MWGui
         ItemModel::ModelIndex selected = -1;
         // not using mSortFilterModel as we only need sorting, not filtering
         SortFilterItemModel model(new InventoryItemModel(player));
-        model.setSortByType(false);
+        model.setSort(SortFilterItemModel::Sort_None);
         model.update();
         if (model.getItemCount() == 0)
             return;
diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp
index e8e348a8a..6c09007a6 100644
--- a/apps/openmw/mwgui/itemmodel.hpp
+++ b/apps/openmw/mwgui/itemmodel.hpp
@@ -78,6 +78,21 @@ namespace MWGui
         virtual bool onDropItem(const MWWorld::Ptr &item, int count);
         virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
 
+        enum Sort
+        {
+            Sort_None,
+            Sort_Name,
+            Sort_Type,
+            Sort_Weight,
+            Sort_Value,
+            Sort_ValuePerWeight,
+            Sort_Last = Sort_ValuePerWeight,
+        };
+
+        virtual bool canSort() { return false; }
+        virtual Sort getSort() { return Sort_None; }
+        virtual void setSort(Sort sort) {}
+
     private:
         ItemModel(const ItemModel&);
         ItemModel& operator=(const ItemModel&);
diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp
index 94dcc77c5..fc4283b4a 100644
--- a/apps/openmw/mwgui/itemview.cpp
+++ b/apps/openmw/mwgui/itemview.cpp
@@ -13,6 +13,7 @@
 
 #include "itemmodel.hpp"
 #include "itemwidget.hpp"
+#include "sortfilteritemmodel.hpp"
 
 namespace MWGui
 {
@@ -20,6 +21,7 @@ namespace MWGui
 ItemView::ItemView()
     : mModel(nullptr)
     , mScrollView(nullptr)
+    , mSortButton(nullptr)
 {
 }
 
@@ -48,6 +50,26 @@ void ItemView::initialiseOverride()
         throw std::runtime_error("Item view needs a scroll view");
 
     mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);
+
+    {
+        static const int width  = 60;
+        static const int height = 44;
+        MyGUI::IntCoord coord = MyGUI::IntCoord(
+            mScrollView->getLeft() + mScrollView->getWidth () -  5 - width,
+            mScrollView->getTop ()                            +  5,
+            width,
+            height
+        );
+        mSortButton = createWidget<MyGUI::Button>(
+            MyGUI::WidgetStyle::Child,
+            "MW_Button",
+            coord,
+            MyGUI::Align::Top | MyGUI::Align::Right,
+            "");
+        mSortButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSort);
+        mSortButton->setNeedMouseFocus (true);
+        mSortButton->setNeedKeyFocus (false);
+    }
 }
 
 void ItemView::layoutWidgets()
@@ -130,6 +152,30 @@ void ItemView::update()
     }
 
     layoutWidgets();
+
+    mSortButton->setVisible(mModel->canSort());
+    if (mModel->canSort())
+        switch (mModel->getSort())
+        {
+            case MWGui::ItemModel::Sort_None:
+                mSortButton->setCaption("Sort\n[none]");
+                break;
+            case MWGui::ItemModel::Sort_Name:
+                mSortButton->setCaption("Sort\n[name]");
+                break;
+            case MWGui::ItemModel::Sort_Type:
+                mSortButton->setCaption("Sort\n[type]");
+                break;
+            case MWGui::ItemModel::Sort_Weight:
+                mSortButton->setCaption("Sort\n[wt]");
+                break;
+            case MWGui::ItemModel::Sort_Value:
+                mSortButton->setCaption("Sort\n[value]");
+                break;
+            case MWGui::ItemModel::Sort_ValuePerWeight:
+                mSortButton->setCaption("Sort\n[$/wt]");
+                break;
+        }
 }
 
 void ItemView::resetScrollBars()
@@ -156,6 +202,17 @@ void ItemView::onMouseWheelMoved(MyGUI::Widget *_sender, int _rel)
         mScrollView->setViewOffset(MyGUI::IntPoint(static_cast<int>(mScrollView->getViewOffset().left + _rel*0.3f), 0));
 }
 
+void ItemView::onSort(MyGUI::Widget *sender)
+{
+    if (!mModel || !mModel->canSort())
+        return;
+
+    MWGui::ItemModel::Sort sort = mModel->getSort();
+    sort = (MWGui::ItemModel::Sort)((sort + 1) % (MWGui::ItemModel::Sort_Last + 1));
+    mModel->setSort(sort);
+    update();
+}
+
 void ItemView::setSize(const MyGUI::IntSize &_value)
 {
     bool changed = (_value.width != getWidth() || _value.height != getHeight());
diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp
index fa6ef29f9..11af3001c 100644
--- a/apps/openmw/mwgui/itemview.hpp
+++ b/apps/openmw/mwgui/itemview.hpp
@@ -43,9 +43,11 @@ namespace MWGui
         void onSelectedItem (MyGUI::Widget* sender);
         void onSelectedBackground (MyGUI::Widget* sender);
         void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel);
+        void onSort(MyGUI::Widget* _sender);
 
         ItemModel* mModel;
         MyGUI::ScrollView* mScrollView;
+        MyGUI::Button* mSortButton;
 
     };
 
diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp
index 23f8a121b..cb24692db 100644
--- a/apps/openmw/mwgui/sortfilteritemmodel.cpp
+++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp
@@ -48,17 +48,60 @@ namespace
         return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2);
     }
 
+    float stackWeight(const MWGui::ItemStack& stack)
+    {
+        return stack.mBase.getClass().getWeight(stack.mBase) * stack.mCount;
+    }
+
+    std::string stackName(const MWGui::ItemStack& stack)
+    {
+        return stack.mBase.getClass().getName(stack.mBase);
+    }
+
+    float stackValue(const MWGui::ItemStack& stack)
+    {
+        return stack.mBase.getClass().getValue(stack.mBase) * stack.mCount;
+    }
+
+    float stackValuePerWeight(const MWGui::ItemStack& stack)
+    {
+        float weight = stackWeight(stack);
+        if (weight < 1e-6f)
+            weight = 1e-6f;
+        return stackValue(stack) / weight;
+    }
+
     struct Compare
     {
-        bool mSortByType;
-        Compare() : mSortByType(true) {}
+        MWGui::SortFilterItemModel::Sort mSort;
+        Compare() : mSort(MWGui::SortFilterItemModel::Sort_Type) {}
         bool operator() (const MWGui::ItemStack& left, const MWGui::ItemStack& right)
         {
-            if (mSortByType && left.mType != right.mType)
-                return left.mType < right.mType;
-
             float result = 0;
 
+            switch (mSort)
+            {
+                case MWGui::SortFilterItemModel::Sort_None:
+                    break;
+                case MWGui::SortFilterItemModel::Sort_Name:
+                    result = stackName(left).compare(stackName(right));
+                    break;
+                case MWGui::SortFilterItemModel::Sort_Type:
+                    result = left.mType - right.mType;
+                    break;
+                case MWGui::SortFilterItemModel::Sort_Weight:
+                    result = stackWeight(left) - stackWeight(right);
+                    break;
+                case MWGui::SortFilterItemModel::Sort_Value:
+                    result = stackValue(left) - stackValue(right);
+                    break;
+                case MWGui::SortFilterItemModel::Sort_ValuePerWeight:
+                    result = stackValuePerWeight(left) - stackValuePerWeight(right);
+                    break;
+            }
+            if (result != 0)
+                return result < 0;
+
             // compare items by type
             std::string leftName = left.mBase.getTypeName();
             std::string rightName = right.mBase.getTypeName();
@@ -150,7 +193,7 @@ namespace MWGui
     SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel)
         : mCategory(Category_All)
         , mFilter(0)
-        , mSortByType(true)
+        , mSort(Sort_Type)
     {
         mSourceModel = sourceModel;
     }
@@ -303,7 +346,7 @@ namespace MWGui
         }
 
         Compare cmp;
-        cmp.mSortByType = mSortByType;
+        cmp.mSort = mSort;
         std::sort(mItems.begin(), mItems.end(), cmp);
     }
 
diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp
index 98da8d8c9..c06bb63de 100644
--- a/apps/openmw/mwgui/sortfilteritemmodel.hpp
+++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp
@@ -27,7 +27,9 @@ namespace MWGui
         void setFilter (int filter);
 
         /// Use ItemStack::Type for sorting?
-        void setSortByType(bool sort) { mSortByType = sort; }
+        bool canSort() { return true; }
+        Sort getSort() { return mSort; }
+        void setSort(Sort sort) { mSort = sort; }
 
         void onClose();
         bool onDropItem(const MWWorld::Ptr &item, int count);
@@ -56,7 +58,7 @@ namespace MWGui
 
         int mCategory;
         int mFilter;
-        bool mSortByType;
+        Sort mSort;
     };
 
 }

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 16:30
by Chris
This kind of thing would certainly be very useful. My only suggestion(s) would be, the toggle box probably shouldn't be in the inventory space where the icons are. Perhaps a pull-down widget next to the filter buttons? Also, a toggle for ascending/descending would be helpful, too.

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 16:34
by CyberShadow
Chris wrote: 04 Mar 2019, 16:30This kind of thing would certainly be very useful.
As an official feature? I did not think of it as such.

The reason that the button is in the inventory space is because it is part of the item view control. Doing it the other way would require editing the layouts of every window that item views can appear in (making the patch bigger). That's not an obstacle for an official feature, though.

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 18:18
by lysol
CyberShadow wrote: 04 Mar 2019, 16:34
Chris wrote: 04 Mar 2019, 16:30This kind of thing would certainly be very useful.
As an official feature?
Yes please.

Oh and Chris' suggestion sounds reasonable.

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 18:51
by CyberShadow
Well, I just saw the "Feature additions policy" in the CONTRIBUTING document, and (especially viz. "new game UI strings") it doesn't look like such patches would be in line with the project's current goals.

https://github.com/OpenMW/openmw/blob/m ... IBUTING.md

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 18:52
by wareya
Those policies can be overridden if the feature is sufficiently desirable, like important options UI additions.

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 18:56
by lysol
I mean, sure, the project has certain goals and a planned future for what should be added and when etc, but why not just submit a PR? The worst thing that could happen would be if someone said "well, it looks cool, but the engine is not ready for it yet" and then the closed the PR.

In other words, just go for it.

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 19:43
by CyberShadow
Thank you for the feedback. I've submitted the patch as a pull request: https://github.com/OpenMW/openmw/pull/2208

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 19:46
by akortunov
I'd prefer to have a framework to customize GUI rather than a lot of different hardcoded options (especially since Zini do not want to add a way to localize new strings before 1.0).

Re: [PATCH] Inventory sort button

Posted: 04 Mar 2019, 20:13
by raevol
akortunov wrote: 04 Mar 2019, 19:46 I'd prefer to have a framework to customize GUI rather than a lot of different hardcoded options
This, please!