Merge branch 'what-a-selection-you-have' into 'master'

Feat(CS): Add More Selection Controls

See merge request OpenMW/openmw!3674
macos_ci_fix
psi29a 5 months ago
commit cb24475662

@ -25,6 +25,7 @@
#include <components/esm3/loadsoun.hpp>
#include <components/esm3/loadspel.hpp>
#include <components/esm3/loadsscr.hpp>
#include <components/esm3/selectiongroup.hpp>
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
@ -52,6 +53,9 @@ CSMDoc::Saving::Saving(Document& document, const std::filesystem::path& projectP
appendStage(new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script>>(
mDocument.getData().getScripts(), mState, CSMWorld::Scope_Project));
appendStage(new WriteCollectionStage<CSMWorld::IdCollection<ESM::SelectionGroup>>(
mDocument.getData().getSelectionGroups(), mState, CSMWorld::Scope_Project));
appendStage(new CloseSaveStage(mState));
// save content file

@ -415,6 +415,29 @@ void CSMPrefs::State::declare()
declareShortcut("scene-focus-toolbar", "Toggle Toolbar Focus", QKeySequence(Qt::Key_T));
declareShortcut("scene-render-stats", "Debug Rendering Stats", QKeySequence(Qt::Key_F3));
declareShortcut("scene-duplicate", "Duplicate Instance", QKeySequence(Qt::ShiftModifier | Qt::Key_C));
declareShortcut("scene-clear-selection", "Clear Selection", QKeySequence(Qt::Key_Space));
declareShortcut("scene-unhide-all", "Unhide All Objects", QKeySequence(Qt::AltModifier | Qt::Key_H));
declareShortcut("scene-toggle-visibility", "Toggle Selection Visibility", QKeySequence(Qt::Key_H));
declareShortcut("scene-group-1", "Select Group 1", QKeySequence(Qt::Key_1));
declareShortcut("scene-save-1", "Save Group 1", QKeySequence(Qt::ControlModifier | Qt::Key_1));
declareShortcut("scene-group-2", "Select Group 2", QKeySequence(Qt::Key_2));
declareShortcut("scene-save-2", "Save Group 2", QKeySequence(Qt::ControlModifier | Qt::Key_2));
declareShortcut("scene-group-3", "Select Group 3", QKeySequence(Qt::Key_3));
declareShortcut("scene-save-3", "Save Group 3", QKeySequence(Qt::ControlModifier | Qt::Key_3));
declareShortcut("scene-group-4", "Select Group 4", QKeySequence(Qt::Key_4));
declareShortcut("scene-save-4", "Save Group 4", QKeySequence(Qt::ControlModifier | Qt::Key_4));
declareShortcut("scene-group-5", "Selection Group 5", QKeySequence(Qt::Key_5));
declareShortcut("scene-save-5", "Save Group 5", QKeySequence(Qt::ControlModifier | Qt::Key_5));
declareShortcut("scene-group-6", "Selection Group 6", QKeySequence(Qt::Key_6));
declareShortcut("scene-save-6", "Save Group 6", QKeySequence(Qt::ControlModifier | Qt::Key_6));
declareShortcut("scene-group-7", "Selection Group 7", QKeySequence(Qt::Key_7));
declareShortcut("scene-save-7", "Save Group 7", QKeySequence(Qt::ControlModifier | Qt::Key_7));
declareShortcut("scene-group-8", "Selection Group 8", QKeySequence(Qt::Key_8));
declareShortcut("scene-save-8", "Save Group 8", QKeySequence(Qt::ControlModifier | Qt::Key_8));
declareShortcut("scene-group-9", "Selection Group 9", QKeySequence(Qt::Key_9));
declareShortcut("scene-save-9", "Save Group 9", QKeySequence(Qt::ControlModifier | Qt::Key_9));
declareShortcut("scene-group-0", "Selection Group 10", QKeySequence(Qt::Key_0));
declareShortcut("scene-save-0", "Save Group 10", QKeySequence(Qt::ControlModifier | Qt::Key_0));
declareSubcategory("1st/Free Camera");
declareShortcut("free-forward", "Forward", QKeySequence(Qt::Key_W));

@ -477,6 +477,29 @@ namespace CSMPrefs
Settings::SettingValue<std::string> mSceneEditAbort{ mIndex, sName, "scene-edit-abort", "Escape" };
Settings::SettingValue<std::string> mSceneFocusToolbar{ mIndex, sName, "scene-focus-toolbar", "T" };
Settings::SettingValue<std::string> mSceneRenderStats{ mIndex, sName, "scene-render-stats", "F3" };
Settings::SettingValue<std::string> mSceneClearSelection{ mIndex, sName, "scene-clear-selection", "Space" };
Settings::SettingValue<std::string> mSceneUnhideAll{ mIndex, sName, "scene-unhide-all", "Alt+H" };
Settings::SettingValue<std::string> mSceneToggleHidden{ mIndex, sName, "scene-toggle-visibility", "H" };
Settings::SettingValue<std::string> mSceneSelectGroup1{ mIndex, sName, "scene-group-1", "1" };
Settings::SettingValue<std::string> mSceneSaveGroup1{ mIndex, sName, "scene-save-1", "Ctrl+1" };
Settings::SettingValue<std::string> mSceneSelectGroup2{ mIndex, sName, "scene-group-2", "2" };
Settings::SettingValue<std::string> mSceneSaveGroup2{ mIndex, sName, "scene-save-2", "Ctrl+2" };
Settings::SettingValue<std::string> mSceneSelectGroup3{ mIndex, sName, "scene-group-3", "3" };
Settings::SettingValue<std::string> mSceneSaveGroup3{ mIndex, sName, "scene-save-3", "Ctrl+3" };
Settings::SettingValue<std::string> mSceneSelectGroup4{ mIndex, sName, "scene-group-4", "4" };
Settings::SettingValue<std::string> mSceneSaveGroup4{ mIndex, sName, "scene-save-4", "Ctrl+4" };
Settings::SettingValue<std::string> mSceneSelectGroup5{ mIndex, sName, "scene-group-5", "5" };
Settings::SettingValue<std::string> mSceneSaveGroup5{ mIndex, sName, "scene-save-5", "Ctrl+5" };
Settings::SettingValue<std::string> mSceneSelectGroup6{ mIndex, sName, "scene-group-6", "6" };
Settings::SettingValue<std::string> mSceneSaveGroup6{ mIndex, sName, "scene-save-6", "Ctrl+6" };
Settings::SettingValue<std::string> mSceneSelectGroup7{ mIndex, sName, "scene-group-7", "7" };
Settings::SettingValue<std::string> mSceneSaveGroup7{ mIndex, sName, "scene-save-7", "Ctrl+7" };
Settings::SettingValue<std::string> mSceneSelectGroup8{ mIndex, sName, "scene-group-8", "8" };
Settings::SettingValue<std::string> mSceneSaveGroup8{ mIndex, sName, "scene-save-8", "Ctrl+8" };
Settings::SettingValue<std::string> mSceneSelectGroup9{ mIndex, sName, "scene-group-9", "9" };
Settings::SettingValue<std::string> mSceneSaveGroup9{ mIndex, sName, "scene-save-9", "Ctrl+9" };
Settings::SettingValue<std::string> mSceneSelectGroup10{ mIndex, sName, "scene-group-0", "0" };
Settings::SettingValue<std::string> mSceneSaveGroup10{ mIndex, sName, "scene-save-0", "Ctrl+0" };
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };

@ -333,6 +333,37 @@ namespace CSMWorld
return true;
}
SelectionGroupColumn::SelectionGroupColumn()
: Column<ESM::SelectionGroup>(Columns::ColumnId_SelectionGroupObjects, ColumnBase::Display_None)
{
}
QVariant SelectionGroupColumn::get(const Record<ESM::SelectionGroup>& record) const
{
QVariant data;
QStringList selectionInfo;
const std::vector<std::string>& instances = record.get().selectedInstances;
for (const std::string& instance : instances)
selectionInfo << QString::fromStdString(instance);
data.setValue(selectionInfo);
return data;
}
void SelectionGroupColumn::set(Record<ESM::SelectionGroup>& record, const QVariant& data)
{
ESM::SelectionGroup record2 = record.get();
for (const auto& item : data.toStringList())
record2.selectedInstances.push_back(item.toStdString());
record.setModified(record2);
}
bool SelectionGroupColumn::isEditable() const
{
return false;
}
std::optional<std::uint32_t> getSkillIndex(std::string_view value)
{
int index = ESM::Skill::refIdToIndex(ESM::RefId::stringRefId(value));

@ -16,6 +16,7 @@
#include <components/esm3/loadinfo.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/esm3/selectiongroup.hpp>
#include <components/esm3/variant.hpp>
#include <optional>
@ -2391,6 +2392,17 @@ namespace CSMWorld
void set(Record<ESM::BodyPart>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct SelectionGroupColumn : public Column<ESM::SelectionGroup>
{
SelectionGroupColumn();
QVariant get(const Record<ESM::SelectionGroup>& record) const override;
void set(Record<ESM::SelectionGroup>& record, const QVariant& data) override;
bool isEditable() const override;
};
}
// This is required to access the type as a QVariant.

@ -347,6 +347,8 @@ namespace CSMWorld
ColumnId_LevelledCreatureId = 315,
ColumnId_SelectionGroupObjects = 316,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

@ -620,6 +620,10 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mDebugProfiles.addColumn(new DescriptionColumn<ESM::DebugProfile>);
mDebugProfiles.addColumn(new ScriptColumn<ESM::DebugProfile>(ScriptColumn<ESM::DebugProfile>::Type_Lines));
mSelectionGroups.addColumn(new StringIdColumn<ESM::SelectionGroup>);
mSelectionGroups.addColumn(new RecordStateColumn<ESM::SelectionGroup>);
mSelectionGroups.addColumn(new SelectionGroupColumn);
mMetaData.appendBlankRecord(ESM::RefId::stringRefId("sys::meta"));
mMetaData.addColumn(new StringIdColumn<MetaData>(true));
@ -664,6 +668,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
addModel(new ResourceTable(&mResourcesManager.get(UniversalId::Type_Textures)), UniversalId::Type_Texture);
addModel(new ResourceTable(&mResourcesManager.get(UniversalId::Type_Videos)), UniversalId::Type_Video);
addModel(new IdTable(&mMetaData), UniversalId::Type_MetaData);
addModel(new IdTable(&mSelectionGroups), UniversalId::Type_SelectionGroup);
mActorAdapter = std::make_unique<ActorAdapter>(*this);
@ -908,6 +913,16 @@ CSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles()
return mDebugProfiles;
}
CSMWorld::IdCollection<ESM::SelectionGroup>& CSMWorld::Data::getSelectionGroups()
{
return mSelectionGroups;
}
const CSMWorld::IdCollection<ESM::SelectionGroup>& CSMWorld::Data::getSelectionGroups() const
{
return mSelectionGroups;
}
const CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const
{
return mLand;
@ -1369,6 +1384,17 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
mDebugProfiles.load(*mReader, mBase);
break;
case ESM::REC_SELG:
if (!mProject)
{
unhandledRecord = true;
break;
}
mSelectionGroups.load(*mReader, mBase);
break;
default:
unhandledRecord = true;

@ -33,6 +33,7 @@
#include <components/esm3/loadsoun.hpp>
#include <components/esm3/loadspel.hpp>
#include <components/esm3/loadsscr.hpp>
#include <components/esm3/selectiongroup.hpp>
#include <components/files/multidircollection.hpp>
#include <components/misc/algorithm.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -105,6 +106,7 @@ namespace CSMWorld
IdCollection<ESM::BodyPart> mBodyParts;
IdCollection<ESM::MagicEffect> mMagicEffects;
IdCollection<ESM::DebugProfile> mDebugProfiles;
IdCollection<ESM::SelectionGroup> mSelectionGroups;
IdCollection<ESM::SoundGenerator> mSoundGens;
IdCollection<ESM::StartScript> mStartScripts;
NestedInfoCollection mTopicInfos;
@ -251,6 +253,10 @@ namespace CSMWorld
IdCollection<ESM::DebugProfile>& getDebugProfiles();
const IdCollection<ESM::SelectionGroup>& getSelectionGroups() const;
IdCollection<ESM::SelectionGroup>& getSelectionGroups();
const IdCollection<CSMWorld::Land>& getLand() const;
IdCollection<CSMWorld::Land>& getLand();

@ -68,6 +68,7 @@ namespace
":./resources-video" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles",
":./debug-profile.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SelectionGroup, "Selection Groups", "" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators",
":./sound-generator.png" },

@ -133,6 +133,7 @@ namespace CSMWorld
Type_LandTexture,
Type_Pathgrids,
Type_Pathgrid,
Type_SelectionGroup,
Type_StartScripts,
Type_StartScript,
Type_Search,

@ -612,6 +612,30 @@ osg::ref_ptr<CSVRender::TagBase> CSVRender::Cell::getSnapTarget(unsigned int ele
return result;
}
void CSVRender::Cell::selectFromGroup(const std::vector<std::string> group)
{
for (const auto& [_, object] : mObjects)
{
for (const auto& objectName : group)
{
if (objectName == object->getReferenceId())
{
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
}
}
}
}
void CSVRender::Cell::unhideAll()
{
for (const auto& [_, object] : mObjects)
{
osg::ref_ptr<osg::Group> rootNode = object->getRootNode();
if (rootNode->getNodeMask() == Mask_Hidden)
rootNode->setNodeMask(Mask_Reference);
}
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::Cell::getSelection(unsigned int elementMask) const
{
std::vector<osg::ref_ptr<TagBase>> result;

@ -148,6 +148,10 @@ namespace CSVRender
// already selected
void selectAllWithSameParentId(int elementMask);
void selectFromGroup(const std::vector<std::string> group);
void unhideAll();
void handleSelectDrag(Object* object, DragMode dragMode);
void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode);

@ -186,6 +186,71 @@ osg::Vec3f CSVRender::InstanceMode::getMousePlaneCoords(const QPoint& point, con
return mousePlanePoint;
}
void CSVRender::InstanceMode::saveSelectionGroup(const int group)
{
QStringList strings;
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QVariant selectionObjects;
CSMWorld::CommandMacro macro(undoStack, "Replace Selection Group");
std::string groupName = "project::" + std::to_string(group);
const auto& selection = getWorldspaceWidget().getSelection(Mask_Reference);
const int selectionObjectsIndex
= mSelectionGroups->findColumnIndex(CSMWorld::Columns::ColumnId_SelectionGroupObjects);
if (dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget()))
groupName += "-ext";
else
groupName += "-" + getWorldspaceWidget().getCellId(osg::Vec3f(0, 0, 0));
CSMWorld::CreateCommand* newGroup = new CSMWorld::CreateCommand(*mSelectionGroups, groupName);
newGroup->setType(CSMWorld::UniversalId::Type_SelectionGroup);
for (const auto& object : selection)
if (const CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(object.get()))
strings << QString::fromStdString(objectTag->mObject->getReferenceId());
selectionObjects.setValue(strings);
newGroup->addValue(selectionObjectsIndex, selectionObjects);
if (mSelectionGroups->getModelIndex(groupName, 0).row() != -1)
macro.push(new CSMWorld::DeleteCommand(*mSelectionGroups, groupName));
macro.push(newGroup);
getWorldspaceWidget().clearSelection(Mask_Reference);
}
void CSVRender::InstanceMode::getSelectionGroup(const int group)
{
std::string groupName = "project::" + std::to_string(group);
std::vector<std::string> targets;
const auto& selection = getWorldspaceWidget().getSelection(Mask_Reference);
const int selectionObjectsIndex
= mSelectionGroups->findColumnIndex(CSMWorld::Columns::ColumnId_SelectionGroupObjects);
if (dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget()))
groupName += "-ext";
else
groupName += "-" + getWorldspaceWidget().getCellId(osg::Vec3f(0, 0, 0));
const QModelIndex groupSearch = mSelectionGroups->getModelIndex(groupName, selectionObjectsIndex);
if (groupSearch.row() == -1)
return;
for (const QString& target : groupSearch.data().toStringList())
targets.push_back(target.toStdString());
if (!selection.empty())
getWorldspaceWidget().clearSelection(Mask_Reference);
getWorldspaceWidget().selectGroup(targets);
}
CSVRender::InstanceMode::InstanceMode(
WorldspaceWidget* worldspaceWidget, osg::ref_ptr<osg::Group> parentNode, QWidget* parent)
: EditMode(worldspaceWidget, QIcon(":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain,
@ -199,6 +264,9 @@ CSVRender::InstanceMode::InstanceMode(
, mUnitScaleDist(1)
, mParentNode(std::move(parentNode))
{
mSelectionGroups = dynamic_cast<CSMWorld::IdTable*>(
worldspaceWidget->getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_SelectionGroup));
connect(this, &InstanceMode::requestFocus, worldspaceWidget, &WorldspaceWidget::requestFocus);
CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget);
@ -229,6 +297,14 @@ CSVRender::InstanceMode::InstanceMode(
= new CSMPrefs::Shortcut("scene-instance-drop-terrain-separately", worldspaceWidget);
connect(dropToTerrainLevelShortcut2, qOverload<>(&CSMPrefs::Shortcut::activated), this,
&InstanceMode::dropSelectedInstancesToTerrainSeparately);
for (short i = 0; i <= 9; i++)
{
connect(new CSMPrefs::Shortcut("scene-group-" + std::to_string(i), worldspaceWidget),
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, i] { this->getSelectionGroup(i); });
connect(new CSMPrefs::Shortcut("scene-save-" + std::to_string(i), worldspaceWidget),
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, i] { this->saveSelectionGroup(i); });
}
}
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)

@ -14,6 +14,8 @@
#include "editmode.hpp"
#include "instancedragmodes.hpp"
#include <apps/opencs/model/world/idtable.hpp>
#include <components/esm3/selectiongroup.hpp>
class QDragEnterEvent;
class QDropEvent;
@ -60,6 +62,7 @@ namespace CSVRender
osg::ref_ptr<osg::Group> mParentNode;
osg::Vec3 mDragStart;
std::vector<osg::Vec3> mObjectsAtDragStart;
CSMWorld::IdTable* mSelectionGroups;
int getSubModeFromId(const std::string& id) const;
@ -133,6 +136,8 @@ namespace CSVRender
void subModeChanged(const std::string& id);
void deleteSelectedInstances();
void cloneSelectedInstances();
void getSelectionGroup(const int group);
void saveSelectionGroup(const int group);
void dropSelectedInstancesToCollision();
void dropSelectedInstancesToTerrain();
void dropSelectedInstancesToCollisionSeparately();

@ -11,6 +11,7 @@ namespace CSVRender
enum Mask : unsigned int
{
// elements that are part of the actual scene
Mask_Hidden = 0x0,
Mask_Reference = 0x2,
Mask_Pathgrid = 0x4,
Mask_Water = 0x8,

@ -33,7 +33,6 @@
#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Vec3>
#include <osg/Vec4f>
#include <osgFX/Scribe>
@ -485,7 +484,7 @@ CSVRender::Object::~Object()
mParentNode->removeChild(mRootNode);
}
void CSVRender::Object::setSelected(bool selected)
void CSVRender::Object::setSelected(bool selected, osg::Vec4f color)
{
mSelected = selected;
@ -499,7 +498,7 @@ void CSVRender::Object::setSelected(bool selected)
mRootNode->removeChild(mBaseNode);
if (selected)
{
mOutline->setWireframeColor(osg::Vec4f(1, 1, 1, 1));
mOutline->setWireframeColor(color);
mOutline->addChild(mBaseNode);
mRootNode->addChild(mOutline);
}

@ -5,6 +5,7 @@
#include <string>
#include <osg/Vec3f>
#include <osg/Vec4f>
#include <osg/ref_ptr>
#include <components/esm/defs.hpp>
@ -138,7 +139,7 @@ namespace CSVRender
~Object();
/// Mark the object as selected, selected objects show an outline effect
void setSelected(bool selected);
void setSelected(bool selected, osg::Vec4f color = osg::Vec4f(1, 1, 1, 1));
bool getSelected() const;

@ -875,6 +875,18 @@ std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::PagedWorldspaceWidget::
return result;
}
void CSVRender::PagedWorldspaceWidget::selectGroup(std::vector<std::string> group) const
{
for (const auto& [_, cell] : mCells)
cell->selectFromGroup(group);
}
void CSVRender::PagedWorldspaceWidget::unhideAll() const
{
for (const auto& [_, cell] : mCells)
cell->unhideAll();
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::PagedWorldspaceWidget::getEdited(
unsigned int elementMask) const
{

@ -163,6 +163,10 @@ namespace CSVRender
std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const override;
void selectGroup(const std::vector<std::string> group) const override;
void unhideAll() const override;
std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const override;
void setSubMode(int subMode, unsigned int elementMask) override;

@ -199,6 +199,16 @@ std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget
return mCell->getSelection(elementMask);
}
void CSVRender::UnpagedWorldspaceWidget::selectGroup(const std::vector<std::string> group) const
{
mCell->selectFromGroup(group);
}
void CSVRender::UnpagedWorldspaceWidget::unhideAll() const
{
mCell->unhideAll();
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget::getEdited(
unsigned int elementMask) const
{

@ -93,6 +93,10 @@ namespace CSVRender
std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const override;
void selectGroup(const std::vector<std::string> group) const override;
void unhideAll() const override;
std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const override;
void setSubMode(int subMode, unsigned int elementMask) override;

@ -50,6 +50,7 @@
#include "cameracontroller.hpp"
#include "instancemode.hpp"
#include "mask.hpp"
#include "object.hpp"
#include "pathgridmode.hpp"
@ -135,6 +136,15 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
CSMPrefs::Shortcut* abortShortcut = new CSMPrefs::Shortcut("scene-edit-abort", this);
connect(abortShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &WorldspaceWidget::abortDrag);
connect(new CSMPrefs::Shortcut("scene-toggle-visibility", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
&WorldspaceWidget::toggleHiddenInstances);
connect(new CSMPrefs::Shortcut("scene-unhide-all", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
&WorldspaceWidget::unhideAll);
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
[this] { this->clearSelection(Mask_Reference); });
mInConstructor = false;
}
@ -740,6 +750,23 @@ void CSVRender::WorldspaceWidget::speedMode(bool activate)
mSpeedMode = activate;
}
void CSVRender::WorldspaceWidget::toggleHiddenInstances()
{
const std::vector<osg::ref_ptr<TagBase>> selection = getSelection(Mask_Reference);
if (selection.empty())
return;
const CSVRender::ObjectTag* firstSelection = dynamic_cast<CSVRender::ObjectTag*>(selection.begin()->get());
const CSVRender::Mask firstMask
= firstSelection->mObject->getRootNode()->getNodeMask() == Mask_Hidden ? Mask_Reference : Mask_Hidden;
for (const auto& object : selection)
if (const auto objectTag = dynamic_cast<CSVRender::ObjectTag*>(object.get()))
objectTag->mObject->getRootNode()->setNodeMask(firstMask);
}
void CSVRender::WorldspaceWidget::handleInteraction(InteractionType type, bool activate)
{
if (activate)

@ -201,6 +201,10 @@ namespace CSVRender
virtual std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const = 0;
virtual void selectGroup(const std::vector<std::string>) const = 0;
virtual void unhideAll() const = 0;
virtual std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const = 0;
virtual void setSubMode(int subMode, unsigned int elementMask) = 0;
@ -300,6 +304,8 @@ namespace CSVRender
void speedMode(bool activate);
void toggleHiddenInstances();
protected slots:
void elementSelectionChanged();

@ -166,7 +166,7 @@ add_component_dir (esm3
inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats
weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache
infoorder timestamp formatversion landrecorddata
infoorder timestamp formatversion landrecorddata selectiongroup
)
add_component_dir (esmterrain

@ -170,6 +170,8 @@ namespace ESM
// format 1
REC_FILT = esm3Recname("FILT"),
REC_DBGP = esm3Recname("DBGP"), ///< only used in project files
REC_SELG = esm3Recname("SELG"),
REC_LUAL = esm3Recname("LUAL"), // LuaScriptsCfg (only in omwgame or omwaddon)
// format 16 - Lua scripts in saved games

@ -0,0 +1,38 @@
#include "selectiongroup.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void SelectionGroup::load(ESMReader& esm, bool& isDeleted)
{
while (esm.hasMoreSubs())
{
esm.getSubName();
switch (esm.retSubName().toInt())
{
case fourCC("SELC"):
mId = esm.getRefId();
break;
case fourCC("SELI"):
selectedInstances.push_back(esm.getRefId().getRefIdString());
break;
default:
esm.fail("Unknown subrecord");
break;
}
}
}
void SelectionGroup::save(ESMWriter& esm, bool isDeleted) const
{
esm.writeHNCRefId("SELC", mId);
for (std::string id : selectedInstances)
esm.writeHNCString("SELI", id);
}
void SelectionGroup::blank() {}
}

@ -0,0 +1,34 @@
#ifndef COMPONENTS_ESM_SELECTIONGROUP_H
#define COMPONENTS_ESM_SELECTIONGROUP_H
#include <string>
#include "components/esm/defs.hpp"
#include "components/esm/refid.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
struct SelectionGroup
{
constexpr static RecNameInts sRecordId = REC_SELG;
static constexpr std::string_view getRecordType() { return "SelectionGroup"; }
uint32_t mRecordFlags = 0;
RefId mId;
std::vector<std::string> selectedInstances;
void load(ESMReader& esm, bool& isDeleted);
void save(ESMWriter& esm, bool isDeleted = false) const;
/// Set record to default state (does not touch the ID).
void blank();
};
}
#endif
Loading…
Cancel
Save