diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 4d191b90a4..4e7770121a 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -59,6 +59,8 @@ void CSMPrefs::State::declare() .setTooltip("Minimum width of subviews.") .setRange(50, 10000); declareEnum(mValues->mWindows.mMainwindowScrollbar, "Main Window Horizontal Scrollbar Mode"); + declareEnum(mValues->mWindows.mSubviewOpenDirection, "Subview Open Direction") + .setTooltip("Controls whether new subviews open to the right or left of existing ones."); declareCategory("Records"); declareEnum(mValues->mRecords.mStatusFormat, "Modification Status Display Format"); diff --git a/apps/opencs/model/prefs/values.hpp b/apps/opencs/model/prefs/values.hpp index 7b6d5e9f5f..3cdfa41e94 100644 --- a/apps/opencs/model/prefs/values.hpp +++ b/apps/opencs/model/prefs/values.hpp @@ -81,6 +81,11 @@ namespace CSMPrefs "Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further." }, }; + static constexpr std::array sSubviewOpenDirectionValues{ + EnumValueView{ "Open Right", "New subviews open to the right of existing ones." }, + EnumValueView{ "Open Left", "New subviews open to the left of existing ones." }, + }; + Settings::SettingValue mDefaultWidth{ mIndex, sName, "default-width", 800 }; Settings::SettingValue mDefaultHeight{ mIndex, sName, "default-height", 600 }; Settings::SettingValue mShowStatusbar{ mIndex, sName, "show-statusbar", true }; @@ -90,6 +95,8 @@ namespace CSMPrefs Settings::SettingValue mMinimumWidth{ mIndex, sName, "minimum-width", 325 }; EnumSettingValue mMainwindowScrollbar{ mIndex, sName, "mainwindow-scrollbar", sMainwindowScrollbarValues, 0 }; Settings::SettingValue mGrowLimit{ mIndex, sName, "grow-limit", false }; + EnumSettingValue mSubviewOpenDirection{ mIndex, sName, "subview-open-direction", sSubviewOpenDirectionValues, + 0 }; }; struct RecordsCategory : Settings::WithIndex diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index ffc7400da3..f419f18fb7 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -663,6 +663,31 @@ void CSVDoc::View::addSubView(const CSMWorld::UniversalId& id, const std::string mSubViewWindow.addDockWidget(Qt::TopDockWidgetArea, view); + // Horizontal orientation doesn't respect layout direction, so we need to move the new view + if (windows["subview-open-direction"].toString() == "Open Left" && mSubViews.size() > 1) + { + // Get list of top-area dock widget subviews sorted by coordinates + QList orderedTopViews; + for (const auto& windowView : mSubViews) + if (windowView != view && mSubViewWindow.dockWidgetArea(windowView) == Qt::TopDockWidgetArea) + orderedTopViews.append(windowView); + if (!orderedTopViews.isEmpty()) + { + std::sort(orderedTopViews.begin(), orderedTopViews.end(), [](auto* a, auto* b) { + if (a->x() != b->x()) + return a->x() < b->x(); + else if (a->y() != b->y()) + return a->y() < b->y(); + return a->getUniversalId() < b->getUniversalId(); + }); + + // Split twice to make the new view the leftmost. + // If the leftmost view is nested, the new view will be added above it. + mSubViewWindow.splitDockWidget(orderedTopViews[0], view, Qt::Orientation::Horizontal); + mSubViewWindow.splitDockWidget(view, orderedTopViews[0], Qt::Orientation::Horizontal); + } + } + updateSubViewIndices(); connect(view, &SubView::focusId, this, &View::addSubView); @@ -706,7 +731,11 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max) { if (mScroll) { - mScroll->horizontalScrollBar()->setValue(max); + CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; + if (windows["subview-open-direction"].toString() == "Open Left") + mScroll->horizontalScrollBar()->setValue(min); + else + mScroll->horizontalScrollBar()->setValue(max); QObject::disconnect(mScroll->horizontalScrollBar(), &QScrollBar::rangeChanged, this, &View::moveScrollBarToEnd); } @@ -1122,7 +1151,8 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth) if (newWidth + frameWidth <= rect.width()) { resize(newWidth, height()); - // WARNING: below code assumes that new subviews are added to the right + // WARNING: below code assumes that the frame geometry expands to the right. + // This doesn't conflict with subview-open-direction. if (x() > rect.width() - (newWidth + frameWidth)) move(rect.width() - (newWidth + frameWidth), y()); // shift left to stay within the screen }