From 912a5d7ab27ef2b6fdc7733113c33bec6541b32a Mon Sep 17 00:00:00 2001 From: apistol78 Date: Mon, 27 May 2024 15:24:03 +0200 Subject: [PATCH] Traktor: Auto edit preview items implemented in PreviewList. --- code/Editor/App/DatabaseView.cpp | 32 +++++++- code/Editor/App/DatabaseView.h | 5 +- .../PreviewList/PreviewContentChangeEvent.cpp | 33 ++++++++ .../PreviewList/PreviewContentChangeEvent.h | 46 +++++++++++ code/Ui/PreviewList/PreviewItem.cpp | 79 ++++++++++++------- code/Ui/PreviewList/PreviewItem.h | 15 +++- code/Ui/PreviewList/PreviewList.cpp | 68 +++++++++++++++- code/Ui/PreviewList/PreviewList.h | 9 +++ 8 files changed, 254 insertions(+), 33 deletions(-) create mode 100644 code/Ui/PreviewList/PreviewContentChangeEvent.cpp create mode 100644 code/Ui/PreviewList/PreviewContentChangeEvent.h diff --git a/code/Editor/App/DatabaseView.cpp b/code/Editor/App/DatabaseView.cpp index 50855c0c28..e0647cc2fc 100644 --- a/code/Editor/App/DatabaseView.cpp +++ b/code/Editor/App/DatabaseView.cpp @@ -51,6 +51,7 @@ #include "Ui/TableLayout.h" #include "Ui/HierarchicalState.h" #include "Ui/Splitter.h" +#include "Ui/PreviewList/PreviewContentChangeEvent.h" #include "Ui/PreviewList/PreviewItem.h" #include "Ui/PreviewList/PreviewItems.h" #include "Ui/PreviewList/PreviewList.h" @@ -403,7 +404,7 @@ bool DatabaseView::create(ui::Widget* parent) m_treeDatabase->addEventHandler< ui::TreeViewItemActivateEvent >(this, &DatabaseView::eventInstanceActivate); m_treeDatabase->addEventHandler< ui::SelectionChangeEvent >(this, &DatabaseView::eventInstanceSelect); m_treeDatabase->addEventHandler< ui::MouseButtonDownEvent >(this, &DatabaseView::eventInstanceButtonDown); - m_treeDatabase->addEventHandler< ui::TreeViewContentChangeEvent >(this, &DatabaseView::eventInstanceRenamed); + m_treeDatabase->addEventHandler< ui::TreeViewContentChangeEvent >(this, &DatabaseView::eventTreeContentChange); m_treeDatabase->addEventHandler< ui::DragEvent >(this, &DatabaseView::eventInstanceDrag); m_treeDatabase->setEnable(false); @@ -412,6 +413,7 @@ bool DatabaseView::create(ui::Widget* parent) return false; m_listInstances->addEventHandler< ui::MouseButtonDownEvent >(this, &DatabaseView::eventInstanceButtonDown); m_listInstances->addEventHandler< ui::MouseDoubleClickEvent >(this, &DatabaseView::eventInstancePreviewActivate); + m_listInstances->addEventHandler< ui::PreviewContentChangeEvent >(this, &DatabaseView::eventPreviewContentChange); m_listInstances->addEventHandler< ui::DragEvent >(this, &DatabaseView::eventInstanceDrag); m_listInstances->setVisible(false); @@ -1637,7 +1639,7 @@ void DatabaseView::eventInstanceButtonDown(ui::MouseButtonDownEvent* event) event->consume(); } -void DatabaseView::eventInstanceRenamed(ui::TreeViewContentChangeEvent* event) +void DatabaseView::eventTreeContentChange(ui::TreeViewContentChangeEvent* event) { Ref< ui::TreeViewItem > treeItem = event->getItem(); if (!treeItem) @@ -1663,6 +1665,32 @@ void DatabaseView::eventInstanceRenamed(ui::TreeViewContentChangeEvent* event) event->consume(); } +void DatabaseView::eventPreviewContentChange(ui::PreviewContentChangeEvent* event) +{ + Ref< ui::PreviewItem > previewItem = event->getItem(); + if (!previewItem) + return; + + Ref< db::Instance > instance = previewItem->getData< db::Instance >(L"INSTANCE"); + Ref< db::Group > group = previewItem->getData< db::Group >(L"GROUP"); + + bool result = false; + + if (instance && group) + { + if (instance->checkout()) + { + result = instance->setName(previewItem->getText()); + result &= instance->commit(); + } + } + else if (group) + result = group->rename(previewItem->getText()); + + if (result) + event->consume(); +} + void DatabaseView::eventInstanceDrag(ui::DragEvent* event) { Ref< db::Instance > instance; diff --git a/code/Editor/App/DatabaseView.h b/code/Editor/App/DatabaseView.h index 5e673111d2..64c4fa97e3 100644 --- a/code/Editor/App/DatabaseView.h +++ b/code/Editor/App/DatabaseView.h @@ -25,6 +25,7 @@ namespace traktor::ui class Edit; class HierarchicalState; class Menu; +class PreviewContentChangeEvent; class PreviewList; class Splitter; class ToolBar; @@ -140,7 +141,9 @@ class DatabaseView : public ui::Container void eventInstanceButtonDown(ui::MouseButtonDownEvent* event); - void eventInstanceRenamed(ui::TreeViewContentChangeEvent* event); + void eventTreeContentChange(ui::TreeViewContentChangeEvent* event); + + void eventPreviewContentChange(ui::PreviewContentChangeEvent* event); void eventInstanceDrag(ui::DragEvent* event); diff --git a/code/Ui/PreviewList/PreviewContentChangeEvent.cpp b/code/Ui/PreviewList/PreviewContentChangeEvent.cpp new file mode 100644 index 0000000000..dad63451d5 --- /dev/null +++ b/code/Ui/PreviewList/PreviewContentChangeEvent.cpp @@ -0,0 +1,33 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "Ui/PreviewList/PreviewContentChangeEvent.h" + +namespace traktor::ui +{ + +T_IMPLEMENT_RTTI_CLASS(L"traktor.ui.PreviewContentChangeEvent", PreviewContentChangeEvent, ContentChangeEvent) + +PreviewContentChangeEvent::PreviewContentChangeEvent(EventSubject* sender, PreviewItem* item, const std::wstring& originalText) +: ContentChangeEvent(sender) +, m_item(item) +, m_originalText(originalText) +{ +} + +PreviewItem* PreviewContentChangeEvent::getItem() const +{ + return m_item; +} + +const std::wstring& PreviewContentChangeEvent::getOriginalText() const +{ + return m_originalText; +} + +} diff --git a/code/Ui/PreviewList/PreviewContentChangeEvent.h b/code/Ui/PreviewList/PreviewContentChangeEvent.h new file mode 100644 index 0000000000..a0f78af84d --- /dev/null +++ b/code/Ui/PreviewList/PreviewContentChangeEvent.h @@ -0,0 +1,46 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include "Ui/Events/ContentChangeEvent.h" + +// import/export mechanism. +#undef T_DLLCLASS +#if defined(T_UI_EXPORT) +# define T_DLLCLASS T_DLLEXPORT +#else +# define T_DLLCLASS T_DLLIMPORT +#endif + +namespace traktor::ui +{ + +class PreviewItem; + +/*! + * \ingroup UI + */ +class T_DLLCLASS PreviewContentChangeEvent : public ContentChangeEvent +{ + T_RTTI_CLASS; + +public: + explicit PreviewContentChangeEvent(EventSubject* sender, PreviewItem* item, const std::wstring& originalText); + + PreviewItem* getItem() const; + + const std::wstring& getOriginalText() const; + +private: + Ref< PreviewItem > m_item; + std::wstring m_originalText; +}; + +} diff --git a/code/Ui/PreviewList/PreviewItem.cpp b/code/Ui/PreviewList/PreviewItem.cpp index c0fefe3880..0022bbdccd 100644 --- a/code/Ui/PreviewList/PreviewItem.cpp +++ b/code/Ui/PreviewList/PreviewItem.cpp @@ -6,6 +6,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#include "Core/Log/Log.h" #include "Ui/Application.h" #include "Ui/Bitmap.h" #include "Ui/Canvas.h" @@ -13,6 +14,7 @@ #include "Ui/StyleSheet.h" #include "Ui/Auto/AutoWidget.h" #include "Ui/PreviewList/PreviewItem.h" +#include "Ui/PreviewList/PreviewList.h" namespace traktor::ui { @@ -20,14 +22,12 @@ namespace traktor::ui T_IMPLEMENT_RTTI_CLASS(L"traktor.ui.PreviewItem", PreviewItem, AutoWidgetCell) PreviewItem::PreviewItem() -: m_selected(false) { m_imageBackground = new ui::StyleBitmap(L"UI.Preview.Background"); } PreviewItem::PreviewItem(const std::wstring& text) : m_text(text) -, m_selected(false) { m_imageBackground = new ui::StyleBitmap(L"UI.Preview.Background"); } @@ -72,6 +72,42 @@ bool PreviewItem::isSelected() const return m_selected; } +void PreviewItem::mouseDown(MouseButtonDownEvent* event, const Point& position) +{ + if (m_editable) + { + if (m_editMode == 0) + { + // Wait for next tap; cancel wait after 2 seconds. + getWidget()->requestInterval(this, 2000); + m_editMode = 1; + } + else if (m_editMode == 1) + { + // Double tap detected; begin edit after mouse is released. + getWidget()->requestInterval(this, 1000); + m_editMode = 2; + } + } +} + +void PreviewItem::mouseUp(MouseButtonUpEvent* event, const Point& position) +{ + PreviewList* list = getWidget< PreviewList >(); + if (m_editMode == 2) + { + T_ASSERT(m_editable); + list->beginEdit(this); + m_editMode = 0; + } +} + +void PreviewItem::mouseDoubleClick(MouseDoubleClickEvent* event, const Point& position) +{ + // Ensure edit isn't triggered. + m_editMode = 0; +} + void PreviewItem::paint(Canvas& canvas, const Rect& rect) { const StyleSheet* ss = getStyleSheet(); @@ -82,9 +118,9 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) Size textExtent = canvas.getFontMetric().getExtent(text); Size subTextExtent = !m_subText.empty() ? canvas.getFontMetric().getExtent(m_subText) : Size(0, 0); - Rect textRect = rect; + m_textRect = rect; - if (textExtent.cx > textRect.getWidth()) + if (textExtent.cx > m_textRect.getWidth()) { if (!isSelected()) { @@ -93,7 +129,7 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) { text = text.substr(0, text.length() - 1); textExtent = canvas.getFontMetric().getExtent(text + L"..."); - if (textExtent.cx <= textRect.getWidth()) + if (textExtent.cx <= m_textRect.getWidth()) break; } text += L"..."; @@ -101,18 +137,18 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) else { // Item is selected; enlarge text rectangle. - const int32_t excess = textExtent.cx - textRect.getWidth(); - textRect.left -= excess / 2 + 5; - textRect.right += (excess + 1) / 2 + 5; + const int32_t excess = textExtent.cx - m_textRect.getWidth(); + m_textRect.left -= excess / 2 + 5; + m_textRect.right += (excess + 1) / 2 + 5; } } - textRect.top = textRect.bottom - textExtent.cy - pixel(4_ut); + m_textRect.top = m_textRect.bottom - textExtent.cy - pixel(4_ut); if (!m_subText.empty()) - textRect.top -= subTextExtent.cy - pixel(4_ut); + m_textRect.top -= subTextExtent.cy - pixel(4_ut); Rect previewRect = rect; - previewRect.bottom = textRect.top; + previewRect.bottom = m_textRect.top; const int32_t dim = std::min(previewRect.getSize().cx, previewRect.getSize().cy) - pixel(8_ut); @@ -124,18 +160,6 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) if (m_bitmapImage) { - //if (m_imageBackground) - //{ - // canvas.drawBitmap( - // thumbPosition, - // thumbSize, - // Point(0, 0), - // m_imageBackground->getSize(getWidget()), - // m_imageBackground, - // BlendMode::Opaque - // ); - //} - canvas.drawBitmap( thumbPosition, thumbSize, @@ -154,12 +178,12 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) if (isSelected()) { canvas.setBackground(ss->getColor(this, L"background-color-selected")); - canvas.fillRect(textRect); + canvas.fillRect(m_textRect); } canvas.setForeground(ss->getColor(this, isSelected() ? L"color-selected" : L"color")); canvas.drawText( - textRect, + m_textRect, text, AnCenter, AnTop @@ -173,11 +197,12 @@ void PreviewItem::paint(Canvas& canvas, const Rect& rect) font.setSize((font.getSize()) * 2_ut / 3_ut); canvas.setFont(font); - textRect.top += textExtent.cy + pixel(2_ut); + Rect subTextRect = m_textRect; + subTextRect.top += textExtent.cy + pixel(2_ut); canvas.setForeground(ss->getColor(this, isSelected() ? L"color-selected" : L"color-subtext")); canvas.drawText( - textRect, + subTextRect, m_subText, AnCenter, AnTop diff --git a/code/Ui/PreviewList/PreviewItem.h b/code/Ui/PreviewList/PreviewItem.h index 4d7e719445..e86a4d86c3 100644 --- a/code/Ui/PreviewList/PreviewItem.h +++ b/code/Ui/PreviewList/PreviewItem.h @@ -49,14 +49,25 @@ class T_DLLCLASS PreviewItem : public AutoWidgetCell bool isSelected() const; - virtual void paint(Canvas& canvas, const Rect& rect) override; + const Rect& getTextRect() const { return m_textRect; } + + virtual void mouseDown(MouseButtonDownEvent* event, const Point& position) override final; + + virtual void mouseUp(MouseButtonUpEvent* event, const Point& position) override final; + + virtual void mouseDoubleClick(MouseDoubleClickEvent* event, const Point& position) override final; + + virtual void paint(Canvas& canvas, const Rect& rect) override final; private: std::wstring m_text; std::wstring m_subText; Ref< ui::StyleBitmap > m_imageBackground; Ref< ui::IBitmap > m_bitmapImage; - bool m_selected; + Rect m_textRect; + bool m_selected = false; + bool m_editable = true; + int32_t m_editMode = 0; }; } diff --git a/code/Ui/PreviewList/PreviewList.cpp b/code/Ui/PreviewList/PreviewList.cpp index 30205d8b9e..23f6946542 100644 --- a/code/Ui/PreviewList/PreviewList.cpp +++ b/code/Ui/PreviewList/PreviewList.cpp @@ -1,12 +1,14 @@ /* * TRAKTOR - * Copyright (c) 2022 Anders Pistol. + * Copyright (c) 2022-2024 Anders Pistol. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "Ui/Application.h" +#include "Ui/Edit.h" +#include "Ui/PreviewList/PreviewContentChangeEvent.h" #include "Ui/PreviewList/PreviewItem.h" #include "Ui/PreviewList/PreviewItems.h" #include "Ui/PreviewList/PreviewList.h" @@ -33,6 +35,12 @@ bool PreviewList::create(Widget* parent, uint32_t style) m_single = bool((style & WsMultiple) == 0); + m_itemEditor = new Edit(); + m_itemEditor->create(this, L"", WsBorder | WsWantAllInput); + m_itemEditor->hide(); + m_itemEditor->addEventHandler< FocusEvent >(this, &PreviewList::eventEditFocus); + m_itemEditor->addEventHandler< KeyDownEvent >(this, &PreviewList::eventEditKeyDownEvent); + addEventHandler< MouseButtonDownEvent >(this, &PreviewList::eventButtonDown); addEventHandler< MouseButtonUpEvent >(this, &PreviewList::eventButtonUp); addEventHandler< MouseMoveEvent >(this, &PreviewList::eventMouseMove); @@ -76,6 +84,19 @@ void PreviewList::getSelectedItems(RefArray< PreviewItem >& outItems) const } } +void PreviewList::beginEdit(PreviewItem* item) +{ + const Rect rcItem = item->getTextRect(); + + m_itemEditor->setRect(rcItem); + m_itemEditor->setText(item->getText()); + m_itemEditor->selectAll(); + m_itemEditor->show(); + m_itemEditor->setFocus(); + + m_editItem = item; +} + void PreviewList::layoutCells(const Rect& rc) { const int32_t nitems = m_items ? m_items->count() : 0; @@ -130,6 +151,51 @@ void PreviewList::layoutCells(const Rect& rc) } } +void PreviewList::eventEditFocus(FocusEvent* event) +{ + if (event->lostFocus() && m_itemEditor->isVisible(false)) + { + const std::wstring originalText = m_editItem->getText(); + const std::wstring newText = m_itemEditor->getText(); + + m_itemEditor->hide(); + + m_editItem->setText(newText); + + PreviewContentChangeEvent changeEvent(this, m_editItem, originalText); + raiseEvent(&changeEvent); + + if (!changeEvent.consumed()) + m_editItem->setText(originalText); + + event->consume(); + } +} + + +void PreviewList::eventEditKeyDownEvent(KeyDownEvent* event) +{ + if (event->getVirtualKey() == ui::VkReturn) + { + const std::wstring originalText = m_editItem->getText(); + const std::wstring newText = m_itemEditor->getText(); + + m_itemEditor->hide(); + + m_editItem->setText(newText); + + PreviewContentChangeEvent changeEvent(this, m_editItem, originalText); + raiseEvent(&changeEvent); + + if (!changeEvent.consumed()) + m_editItem->setText(originalText); + } + else if (event->getVirtualKey() == ui::VkEscape) + { + m_itemEditor->hide(); + } +} + void PreviewList::eventButtonDown(MouseButtonDownEvent* event) { if (event->getButton() != MbtLeft) diff --git a/code/Ui/PreviewList/PreviewList.h b/code/Ui/PreviewList/PreviewList.h index 41add82afa..a610e89a8c 100644 --- a/code/Ui/PreviewList/PreviewList.h +++ b/code/Ui/PreviewList/PreviewList.h @@ -24,6 +24,7 @@ namespace traktor::ui { +class Edit; class PreviewItems; class T_DLLCLASS PreviewList : public ui::AutoWidget @@ -47,14 +48,22 @@ class T_DLLCLASS PreviewList : public ui::AutoWidget void getSelectedItems(RefArray< PreviewItem >& outItems) const; + void beginEdit(PreviewItem* item); + private: + Ref< Edit > m_itemEditor; Ref< PreviewItems > m_items; bool m_single = true; int32_t m_dragMode = 0; Point m_dragOriginPosition; + Ref< PreviewItem > m_editItem; virtual void layoutCells(const Rect& rc) override final; + void eventEditFocus(FocusEvent* event); + + void eventEditKeyDownEvent(KeyDownEvent* event); + void eventButtonDown(MouseButtonDownEvent* event); void eventButtonUp(MouseButtonUpEvent* event);