[PATCH] Inventory sort button

General discussion regarding the OpenMW project.
For technical support, please use the Support subforum.
CyberShadow
Posts: 17
Joined: 03 Mar 2019, 17:35
Gitlab profile: https://gitlab.com/CyberShadow

[PATCH] Inventory sort button

Post 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;
     };
 
 }
Chris
Posts: 1625
Joined: 04 Sep 2011, 08:33

Re: [PATCH] Inventory sort button

Post 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.
CyberShadow
Posts: 17
Joined: 03 Mar 2019, 17:35
Gitlab profile: https://gitlab.com/CyberShadow

Re: [PATCH] Inventory sort button

Post 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.
User avatar
lysol
Posts: 1513
Joined: 26 Mar 2013, 01:48
Location: Sweden

Re: [PATCH] Inventory sort button

Post 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.
CyberShadow
Posts: 17
Joined: 03 Mar 2019, 17:35
Gitlab profile: https://gitlab.com/CyberShadow

Re: [PATCH] Inventory sort button

Post 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
User avatar
wareya
Posts: 338
Joined: 09 May 2015, 13:07

Re: [PATCH] Inventory sort button

Post by wareya »

Those policies can be overridden if the feature is sufficiently desirable, like important options UI additions.
User avatar
lysol
Posts: 1513
Joined: 26 Mar 2013, 01:48
Location: Sweden

Re: [PATCH] Inventory sort button

Post 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.
CyberShadow
Posts: 17
Joined: 03 Mar 2019, 17:35
Gitlab profile: https://gitlab.com/CyberShadow

Re: [PATCH] Inventory sort button

Post by CyberShadow »

Thank you for the feedback. I've submitted the patch as a pull request: https://github.com/OpenMW/openmw/pull/2208
User avatar
akortunov
Posts: 899
Joined: 13 Mar 2017, 13:49
Location: Samara, Russian Federation

Re: [PATCH] Inventory sort button

Post 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).
User avatar
raevol
Posts: 3093
Joined: 07 Aug 2011, 01:12
Location: Caldera

Re: [PATCH] Inventory sort button

Post 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!
Post Reply