Advance new editor hosted panels and state flow
This commit is contained in:
@@ -7,11 +7,9 @@
|
||||
#include <XCEditor/Foundation/UIEditorTheme.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#include <windows.h>
|
||||
@@ -32,7 +30,6 @@ using Widgets::AppendUIEditorTreeViewBackground;
|
||||
using Widgets::AppendUIEditorTreeViewForeground;
|
||||
|
||||
constexpr std::string_view kProjectPanelId = "project";
|
||||
constexpr std::string_view kAssetsRootId = "Assets";
|
||||
constexpr std::size_t kInvalidLayoutIndex = static_cast<std::size_t>(-1);
|
||||
|
||||
constexpr float kBrowserHeaderHeight = 24.0f;
|
||||
@@ -102,122 +99,6 @@ float MeasureTextWidth(
|
||||
return static_cast<float>(text.size()) * fontSize * 0.56f;
|
||||
}
|
||||
|
||||
std::string ToLowerCopy(std::string value) {
|
||||
std::transform(
|
||||
value.begin(),
|
||||
value.end(),
|
||||
value.begin(),
|
||||
[](unsigned char character) {
|
||||
return static_cast<char>(std::tolower(character));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string WideToUtf8(std::wstring_view value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int requiredSize = WideCharToMultiByte(
|
||||
CP_UTF8,
|
||||
0,
|
||||
value.data(),
|
||||
static_cast<int>(value.size()),
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (requiredSize <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result(static_cast<std::size_t>(requiredSize), '\0');
|
||||
WideCharToMultiByte(
|
||||
CP_UTF8,
|
||||
0,
|
||||
value.data(),
|
||||
static_cast<int>(value.size()),
|
||||
result.data(),
|
||||
requiredSize,
|
||||
nullptr,
|
||||
nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string PathToUtf8String(const std::filesystem::path& path) {
|
||||
return WideToUtf8(path.native());
|
||||
}
|
||||
|
||||
std::string NormalizePathSeparators(std::string value) {
|
||||
std::replace(value.begin(), value.end(), '\\', '/');
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string BuildRelativeItemId(
|
||||
const std::filesystem::path& path,
|
||||
const std::filesystem::path& assetsRoot) {
|
||||
const std::filesystem::path relative =
|
||||
std::filesystem::relative(path, assetsRoot.parent_path());
|
||||
const std::string normalized =
|
||||
NormalizePathSeparators(PathToUtf8String(relative.lexically_normal()));
|
||||
return normalized.empty() ? std::string(kAssetsRootId) : normalized;
|
||||
}
|
||||
|
||||
std::string BuildAssetDisplayName(const std::filesystem::path& path, bool directory) {
|
||||
if (directory) {
|
||||
return PathToUtf8String(path.filename());
|
||||
}
|
||||
|
||||
const std::string filename = PathToUtf8String(path.filename());
|
||||
const std::size_t extensionOffset = filename.find_last_of('.');
|
||||
if (extensionOffset == std::string::npos || extensionOffset == 0u) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
return filename.substr(0u, extensionOffset);
|
||||
}
|
||||
|
||||
bool IsMetaFile(const std::filesystem::path& path) {
|
||||
return ToLowerCopy(path.extension().string()) == ".meta";
|
||||
}
|
||||
|
||||
bool HasChildDirectories(const std::filesystem::path& folderPath) {
|
||||
std::error_code errorCode = {};
|
||||
const std::filesystem::directory_iterator end = {};
|
||||
for (std::filesystem::directory_iterator iterator(folderPath, errorCode);
|
||||
!errorCode && iterator != end;
|
||||
iterator.increment(errorCode)) {
|
||||
if (iterator->is_directory(errorCode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> CollectSortedChildDirectories(
|
||||
const std::filesystem::path& folderPath) {
|
||||
std::vector<std::filesystem::path> paths = {};
|
||||
std::error_code errorCode = {};
|
||||
const std::filesystem::directory_iterator end = {};
|
||||
for (std::filesystem::directory_iterator iterator(folderPath, errorCode);
|
||||
!errorCode && iterator != end;
|
||||
iterator.increment(errorCode)) {
|
||||
if (iterator->is_directory(errorCode)) {
|
||||
paths.push_back(iterator->path());
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(
|
||||
paths.begin(),
|
||||
paths.end(),
|
||||
[](const std::filesystem::path& lhs, const std::filesystem::path& rhs) {
|
||||
return ToLowerCopy(PathToUtf8String(lhs.filename())) <
|
||||
ToLowerCopy(PathToUtf8String(rhs.filename()));
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
std::vector<UIInputEvent> FilterProjectPanelInputEvents(
|
||||
const UIRect& bounds,
|
||||
const std::vector<UIInputEvent>& inputEvents,
|
||||
@@ -303,35 +184,6 @@ float ClampNavigationWidth(float value, float totalWidth) {
|
||||
return std::clamp(value, kNavigationMinWidth, maxWidth);
|
||||
}
|
||||
|
||||
std::vector<std::string> BuildBreadcrumbSegments(std::string_view currentFolderId) {
|
||||
std::vector<std::string> segments = {};
|
||||
if (currentFolderId.empty()) {
|
||||
segments.push_back(std::string(kAssetsRootId));
|
||||
return segments;
|
||||
}
|
||||
|
||||
std::size_t segmentStart = 0u;
|
||||
while (segmentStart < currentFolderId.size()) {
|
||||
const std::size_t separator = currentFolderId.find('/', segmentStart);
|
||||
const std::size_t segmentLength =
|
||||
separator == std::string_view::npos
|
||||
? currentFolderId.size() - segmentStart
|
||||
: separator - segmentStart;
|
||||
if (segmentLength > 0u) {
|
||||
segments.emplace_back(currentFolderId.substr(segmentStart, segmentLength));
|
||||
}
|
||||
if (separator == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
segmentStart = separator + 1u;
|
||||
}
|
||||
|
||||
if (segments.empty()) {
|
||||
segments.push_back(std::string(kAssetsRootId));
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
void AppendTilePreview(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& previewRect,
|
||||
@@ -371,18 +223,14 @@ void AppendTilePreview(
|
||||
} // namespace
|
||||
|
||||
void ProductProjectPanel::Initialize(const std::filesystem::path& repoRoot) {
|
||||
m_assetsRootPath = (repoRoot / "project/Assets").lexically_normal();
|
||||
RefreshFolderTree();
|
||||
m_browserModel.Initialize(repoRoot);
|
||||
SyncCurrentFolderSelection();
|
||||
RefreshAssetList();
|
||||
}
|
||||
|
||||
void ProductProjectPanel::SetBuiltInIcons(const ProductBuiltInIcons* icons) {
|
||||
m_icons = icons;
|
||||
if (!m_assetsRootPath.empty()) {
|
||||
RefreshFolderTree();
|
||||
SyncCurrentFolderSelection();
|
||||
}
|
||||
m_browserModel.SetFolderIcon(ResolveFolderIcon(m_icons));
|
||||
SyncCurrentFolderSelection();
|
||||
}
|
||||
|
||||
void ProductProjectPanel::SetTextMeasurer(const UIEditorTextMeasurer* textMeasurer) {
|
||||
@@ -411,24 +259,12 @@ const std::vector<ProductProjectPanel::Event>& ProductProjectPanel::GetFrameEven
|
||||
|
||||
const ProductProjectPanel::FolderEntry* ProductProjectPanel::FindFolderEntry(
|
||||
std::string_view itemId) const {
|
||||
for (const FolderEntry& entry : m_folderEntries) {
|
||||
if (entry.itemId == itemId) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return m_browserModel.FindFolderEntry(itemId);
|
||||
}
|
||||
|
||||
const ProductProjectPanel::AssetEntry* ProductProjectPanel::FindAssetEntry(
|
||||
std::string_view itemId) const {
|
||||
for (const AssetEntry& entry : m_assetEntries) {
|
||||
if (entry.itemId == itemId) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return m_browserModel.FindAssetEntry(itemId);
|
||||
}
|
||||
|
||||
const UIEditorPanelContentHostPanelState* ProductProjectPanel::FindMountedProjectPanel(
|
||||
@@ -444,6 +280,9 @@ const UIEditorPanelContentHostPanelState* ProductProjectPanel::FindMountedProjec
|
||||
|
||||
ProductProjectPanel::Layout ProductProjectPanel::BuildLayout(const UIRect& bounds) const {
|
||||
Layout layout = {};
|
||||
const auto& assetEntries = m_browserModel.GetAssetEntries();
|
||||
const std::vector<ProductProjectPanel::BrowserModel::BreadcrumbSegment> breadcrumbSegments =
|
||||
m_browserModel.BuildBreadcrumbSegments();
|
||||
const float dividerThickness = ResolveUIEditorDockHostMetrics().splitterMetrics.thickness;
|
||||
layout.bounds = UIRect(
|
||||
bounds.x,
|
||||
@@ -496,9 +335,7 @@ ProductProjectPanel::Layout ProductProjectPanel::BuildLayout(const UIRect& bound
|
||||
const float headerRight =
|
||||
layout.browserHeaderRect.x + layout.browserHeaderRect.width - kHeaderHorizontalPadding;
|
||||
float nextItemX = layout.browserHeaderRect.x + kHeaderHorizontalPadding;
|
||||
std::string cumulativeFolderId = {};
|
||||
const std::vector<std::string> segments = BuildBreadcrumbSegments(m_currentFolderId);
|
||||
for (std::size_t index = 0u; index < segments.size(); ++index) {
|
||||
for (std::size_t index = 0u; index < breadcrumbSegments.size(); ++index) {
|
||||
if (index > 0u) {
|
||||
const float separatorWidth =
|
||||
MeasureTextWidth(m_textMeasurer, ">", kHeaderFontSize);
|
||||
@@ -519,15 +356,9 @@ ProductProjectPanel::Layout ProductProjectPanel::BuildLayout(const UIRect& bound
|
||||
nextItemX += separatorWidth + kBreadcrumbSpacing;
|
||||
}
|
||||
|
||||
if (index == 0u) {
|
||||
cumulativeFolderId = segments[index];
|
||||
} else {
|
||||
cumulativeFolderId += "/";
|
||||
cumulativeFolderId += segments[index];
|
||||
}
|
||||
|
||||
const ProductProjectPanel::BrowserModel::BreadcrumbSegment& segment = breadcrumbSegments[index];
|
||||
const float labelWidth =
|
||||
MeasureTextWidth(m_textMeasurer, segments[index], kHeaderFontSize);
|
||||
MeasureTextWidth(m_textMeasurer, segment.label, kHeaderFontSize);
|
||||
const float itemWidth = labelWidth + kBreadcrumbItemPaddingX * 2.0f;
|
||||
const float availableWidth = headerRight - nextItemX;
|
||||
if (availableWidth <= 0.0f) {
|
||||
@@ -535,16 +366,16 @@ ProductProjectPanel::Layout ProductProjectPanel::BuildLayout(const UIRect& bound
|
||||
}
|
||||
|
||||
layout.breadcrumbItems.push_back({
|
||||
segments[index],
|
||||
cumulativeFolderId,
|
||||
segment.label,
|
||||
segment.targetFolderId,
|
||||
UIRect(
|
||||
nextItemX,
|
||||
breadcrumbY,
|
||||
ClampNonNegative((std::min)(itemWidth, availableWidth)),
|
||||
breadcrumbRowHeight),
|
||||
false,
|
||||
index + 1u != segments.size(),
|
||||
index + 1u == segments.size()
|
||||
!segment.current,
|
||||
segment.current
|
||||
});
|
||||
nextItemX += itemWidth + kBreadcrumbSpacing;
|
||||
}
|
||||
@@ -557,8 +388,8 @@ ProductProjectPanel::Layout ProductProjectPanel::BuildLayout(const UIRect& bound
|
||||
columnCount = 1;
|
||||
}
|
||||
|
||||
layout.assetTiles.reserve(m_assetEntries.size());
|
||||
for (std::size_t index = 0; index < m_assetEntries.size(); ++index) {
|
||||
layout.assetTiles.reserve(assetEntries.size());
|
||||
for (std::size_t index = 0; index < assetEntries.size(); ++index) {
|
||||
const int column = static_cast<int>(index % static_cast<std::size_t>(columnCount));
|
||||
const int row = static_cast<int>(index / static_cast<std::size_t>(columnCount));
|
||||
const float tileX = layout.gridRect.x + static_cast<float>(column) * (kGridTileWidth + kGridTileGapX);
|
||||
@@ -604,85 +435,31 @@ std::size_t ProductProjectPanel::HitTestAssetTile(const UIPoint& point) const {
|
||||
return kInvalidLayoutIndex;
|
||||
}
|
||||
|
||||
void ProductProjectPanel::RefreshFolderTree() {
|
||||
m_folderEntries.clear();
|
||||
m_treeItems.clear();
|
||||
|
||||
if (m_assetsRootPath.empty() || !std::filesystem::exists(m_assetsRootPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto appendFolderRecursive =
|
||||
[&](auto&& self, const std::filesystem::path& folderPath, std::uint32_t depth) -> void {
|
||||
const std::string itemId = BuildRelativeItemId(folderPath, m_assetsRootPath);
|
||||
|
||||
FolderEntry folderEntry = {};
|
||||
folderEntry.itemId = itemId;
|
||||
folderEntry.absolutePath = folderPath;
|
||||
m_folderEntries.push_back(std::move(folderEntry));
|
||||
|
||||
Widgets::UIEditorTreeViewItem item = {};
|
||||
item.itemId = itemId;
|
||||
item.label = PathToUtf8String(folderPath.filename());
|
||||
item.depth = depth;
|
||||
item.forceLeaf = !HasChildDirectories(folderPath);
|
||||
item.leadingIcon = ResolveFolderIcon(m_icons);
|
||||
m_treeItems.push_back(std::move(item));
|
||||
|
||||
const std::vector<std::filesystem::path> childFolders =
|
||||
CollectSortedChildDirectories(folderPath);
|
||||
for (const std::filesystem::path& childPath : childFolders) {
|
||||
self(self, childPath, depth + 1u);
|
||||
}
|
||||
};
|
||||
|
||||
appendFolderRecursive(appendFolderRecursive, m_assetsRootPath, 0u);
|
||||
}
|
||||
|
||||
void ProductProjectPanel::EnsureValidCurrentFolder() {
|
||||
if (m_currentFolderId.empty()) {
|
||||
m_currentFolderId = std::string(kAssetsRootId);
|
||||
}
|
||||
|
||||
if (FindFolderEntry(m_currentFolderId) == nullptr && !m_treeItems.empty()) {
|
||||
m_currentFolderId = m_treeItems.front().itemId;
|
||||
}
|
||||
}
|
||||
|
||||
void ProductProjectPanel::ExpandFolderAncestors(std::string_view itemId) {
|
||||
const FolderEntry* folderEntry = FindFolderEntry(itemId);
|
||||
if (folderEntry == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path path = folderEntry->absolutePath;
|
||||
while (true) {
|
||||
m_folderExpansion.Expand(BuildRelativeItemId(path, m_assetsRootPath));
|
||||
if (path == m_assetsRootPath) {
|
||||
break;
|
||||
}
|
||||
path = path.parent_path();
|
||||
}
|
||||
}
|
||||
|
||||
void ProductProjectPanel::SyncCurrentFolderSelection() {
|
||||
EnsureValidCurrentFolder();
|
||||
ExpandFolderAncestors(m_currentFolderId);
|
||||
m_folderSelection.SetSelection(m_currentFolderId);
|
||||
const std::string& currentFolderId = m_browserModel.GetCurrentFolderId();
|
||||
if (currentFolderId.empty()) {
|
||||
m_folderSelection.ClearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<std::string> ancestorFolderIds =
|
||||
m_browserModel.CollectCurrentFolderAncestorIds();
|
||||
for (const std::string& ancestorFolderId : ancestorFolderIds) {
|
||||
m_folderExpansion.Expand(ancestorFolderId);
|
||||
}
|
||||
m_folderSelection.SetSelection(currentFolderId);
|
||||
}
|
||||
|
||||
bool ProductProjectPanel::NavigateToFolder(std::string_view itemId, EventSource source) {
|
||||
if (itemId.empty() || FindFolderEntry(itemId) == nullptr || itemId == m_currentFolderId) {
|
||||
if (!m_browserModel.NavigateToFolder(itemId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentFolderId = std::string(itemId);
|
||||
SyncCurrentFolderSelection();
|
||||
m_assetSelection.ClearSelection();
|
||||
m_hoveredAssetItemId.clear();
|
||||
m_lastPrimaryClickedAssetId.clear();
|
||||
RefreshAssetList();
|
||||
EmitEvent(EventKind::FolderNavigated, source, FindFolderEntry(m_currentFolderId));
|
||||
EmitEvent(EventKind::FolderNavigated, source, FindFolderEntry(m_browserModel.GetCurrentFolderId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -699,7 +476,7 @@ void ProductProjectPanel::EmitEvent(
|
||||
event.source = source;
|
||||
event.itemId = folder->itemId;
|
||||
event.absolutePath = folder->absolutePath;
|
||||
event.displayName = PathToUtf8String(folder->absolutePath.filename());
|
||||
event.displayName = folder->label;
|
||||
event.directory = true;
|
||||
m_frameEvents.push_back(std::move(event));
|
||||
}
|
||||
@@ -731,55 +508,6 @@ void ProductProjectPanel::EmitSelectionClearedEvent(EventSource source) {
|
||||
m_frameEvents.push_back(std::move(event));
|
||||
}
|
||||
|
||||
void ProductProjectPanel::RefreshAssetList() {
|
||||
EnsureValidCurrentFolder();
|
||||
|
||||
m_assetEntries.clear();
|
||||
const FolderEntry* currentFolder = FindFolderEntry(m_currentFolderId);
|
||||
if (currentFolder == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::directory_entry> entries = {};
|
||||
std::error_code errorCode = {};
|
||||
const std::filesystem::directory_iterator end = {};
|
||||
for (std::filesystem::directory_iterator iterator(currentFolder->absolutePath, errorCode);
|
||||
!errorCode && iterator != end;
|
||||
iterator.increment(errorCode)) {
|
||||
if (!iterator->exists(errorCode) || IsMetaFile(iterator->path())) {
|
||||
continue;
|
||||
}
|
||||
if (!iterator->is_directory(errorCode) && !iterator->is_regular_file(errorCode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push_back(*iterator);
|
||||
}
|
||||
|
||||
std::sort(
|
||||
entries.begin(),
|
||||
entries.end(),
|
||||
[](const std::filesystem::directory_entry& lhs, const std::filesystem::directory_entry& rhs) {
|
||||
const bool lhsDirectory = lhs.is_directory();
|
||||
const bool rhsDirectory = rhs.is_directory();
|
||||
if (lhsDirectory != rhsDirectory) {
|
||||
return lhsDirectory && !rhsDirectory;
|
||||
}
|
||||
|
||||
return ToLowerCopy(PathToUtf8String(lhs.path().filename())) <
|
||||
ToLowerCopy(PathToUtf8String(rhs.path().filename()));
|
||||
});
|
||||
|
||||
for (const std::filesystem::directory_entry& entry : entries) {
|
||||
AssetEntry assetEntry = {};
|
||||
assetEntry.itemId = BuildRelativeItemId(entry.path(), m_assetsRootPath);
|
||||
assetEntry.absolutePath = entry.path();
|
||||
assetEntry.displayName = BuildAssetDisplayName(entry.path(), entry.is_directory());
|
||||
assetEntry.directory = entry.is_directory();
|
||||
m_assetEntries.push_back(std::move(assetEntry));
|
||||
}
|
||||
}
|
||||
|
||||
void ProductProjectPanel::ResetTransientFrames() {
|
||||
m_treeFrame = {};
|
||||
m_frameEvents.clear();
|
||||
@@ -811,10 +539,9 @@ void ProductProjectPanel::Update(
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_treeItems.empty()) {
|
||||
RefreshFolderTree();
|
||||
if (m_browserModel.GetTreeItems().empty()) {
|
||||
m_browserModel.Refresh();
|
||||
SyncCurrentFolderSelection();
|
||||
RefreshAssetList();
|
||||
}
|
||||
|
||||
m_visible = true;
|
||||
@@ -836,13 +563,13 @@ void ProductProjectPanel::Update(
|
||||
m_folderSelection,
|
||||
m_folderExpansion,
|
||||
m_layout.treeRect,
|
||||
m_treeItems,
|
||||
m_browserModel.GetTreeItems(),
|
||||
treeEvents,
|
||||
treeMetrics);
|
||||
|
||||
if (m_treeFrame.result.selectionChanged &&
|
||||
!m_treeFrame.result.selectedItemId.empty() &&
|
||||
m_treeFrame.result.selectedItemId != m_currentFolderId) {
|
||||
m_treeFrame.result.selectedItemId != m_browserModel.GetCurrentFolderId()) {
|
||||
NavigateToFolder(m_treeFrame.result.selectedItemId, EventSource::Tree);
|
||||
m_layout = BuildLayout(panelState->bounds);
|
||||
}
|
||||
@@ -871,9 +598,10 @@ void ProductProjectPanel::Update(
|
||||
m_splitterDragging || ContainsPoint(m_layout.dividerRect, event.position);
|
||||
m_hoveredBreadcrumbIndex = HitTestBreadcrumbItem(event.position);
|
||||
const std::size_t hoveredAssetIndex = HitTestAssetTile(event.position);
|
||||
const auto& assetEntries = m_browserModel.GetAssetEntries();
|
||||
m_hoveredAssetItemId =
|
||||
hoveredAssetIndex < m_assetEntries.size()
|
||||
? m_assetEntries[hoveredAssetIndex].itemId
|
||||
hoveredAssetIndex < assetEntries.size()
|
||||
? assetEntries[hoveredAssetIndex].itemId
|
||||
: std::string();
|
||||
break;
|
||||
}
|
||||
@@ -902,8 +630,9 @@ void ProductProjectPanel::Update(
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& assetEntries = m_browserModel.GetAssetEntries();
|
||||
const std::size_t hitIndex = HitTestAssetTile(event.position);
|
||||
if (hitIndex >= m_assetEntries.size()) {
|
||||
if (hitIndex >= assetEntries.size()) {
|
||||
if (m_assetSelection.HasSelection()) {
|
||||
m_assetSelection.ClearSelection();
|
||||
EmitSelectionClearedEvent(EventSource::Background);
|
||||
@@ -911,7 +640,7 @@ void ProductProjectPanel::Update(
|
||||
break;
|
||||
}
|
||||
|
||||
const AssetEntry& assetEntry = m_assetEntries[hitIndex];
|
||||
const AssetEntry& assetEntry = assetEntries[hitIndex];
|
||||
const bool alreadySelected = m_assetSelection.IsSelected(assetEntry.itemId);
|
||||
const bool selectionChanged = m_assetSelection.SetSelection(assetEntry.itemId);
|
||||
if (selectionChanged) {
|
||||
@@ -945,13 +674,17 @@ void ProductProjectPanel::Update(
|
||||
|
||||
if (event.pointerButton == ::XCEngine::UI::UIPointerButton::Right &&
|
||||
ContainsPoint(m_layout.gridRect, event.position)) {
|
||||
const auto& assetEntries = m_browserModel.GetAssetEntries();
|
||||
const std::size_t hitIndex = HitTestAssetTile(event.position);
|
||||
if (hitIndex >= m_assetEntries.size()) {
|
||||
EmitEvent(EventKind::ContextMenuRequested, EventSource::Background, static_cast<const AssetEntry*>(nullptr));
|
||||
if (hitIndex >= assetEntries.size()) {
|
||||
EmitEvent(
|
||||
EventKind::ContextMenuRequested,
|
||||
EventSource::Background,
|
||||
static_cast<const AssetEntry*>(nullptr));
|
||||
break;
|
||||
}
|
||||
|
||||
const AssetEntry& assetEntry = m_assetEntries[hitIndex];
|
||||
const AssetEntry& assetEntry = assetEntries[hitIndex];
|
||||
if (!m_assetSelection.IsSelected(assetEntry.itemId)) {
|
||||
m_assetSelection.SetSelection(assetEntry.itemId);
|
||||
EmitEvent(EventKind::AssetSelected, EventSource::GridSecondary, &assetEntry);
|
||||
@@ -1000,6 +733,8 @@ void ProductProjectPanel::Append(UIDrawList& drawList) const {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& assetEntries = m_browserModel.GetAssetEntries();
|
||||
|
||||
drawList.AddFilledRect(m_layout.bounds, kSurfaceColor);
|
||||
drawList.AddFilledRect(m_layout.leftPaneRect, kPaneColor);
|
||||
drawList.AddFilledRect(m_layout.rightPaneRect, kPaneColor);
|
||||
@@ -1021,7 +756,7 @@ void ProductProjectPanel::Append(UIDrawList& drawList) const {
|
||||
AppendUIEditorTreeViewBackground(
|
||||
drawList,
|
||||
m_treeFrame.layout,
|
||||
m_treeItems,
|
||||
m_browserModel.GetTreeItems(),
|
||||
m_folderSelection,
|
||||
m_treeInteractionState.treeViewState,
|
||||
treePalette,
|
||||
@@ -1029,7 +764,7 @@ void ProductProjectPanel::Append(UIDrawList& drawList) const {
|
||||
AppendUIEditorTreeViewForeground(
|
||||
drawList,
|
||||
m_treeFrame.layout,
|
||||
m_treeItems,
|
||||
m_browserModel.GetTreeItems(),
|
||||
treePalette,
|
||||
treeMetrics);
|
||||
|
||||
@@ -1055,11 +790,11 @@ void ProductProjectPanel::Append(UIDrawList& drawList) const {
|
||||
drawList.PopClipRect();
|
||||
|
||||
for (const AssetTileLayout& tile : m_layout.assetTiles) {
|
||||
if (tile.itemIndex >= m_assetEntries.size()) {
|
||||
if (tile.itemIndex >= assetEntries.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const AssetEntry& assetEntry = m_assetEntries[tile.itemIndex];
|
||||
const AssetEntry& assetEntry = assetEntries[tile.itemIndex];
|
||||
const bool selected = m_assetSelection.IsSelected(assetEntry.itemId);
|
||||
const bool hovered = m_hoveredAssetItemId == assetEntry.itemId;
|
||||
|
||||
@@ -1084,7 +819,7 @@ void ProductProjectPanel::Append(UIDrawList& drawList) const {
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
if (m_assetEntries.empty()) {
|
||||
if (assetEntries.empty()) {
|
||||
const UIRect messageRect(
|
||||
m_layout.gridRect.x,
|
||||
m_layout.gridRect.y,
|
||||
|
||||
Reference in New Issue
Block a user