Skip to content

Commit

Permalink
WTrackProperty: add selected-click in-place editor, add styles for La…
Browse files Browse the repository at this point in the history
…teNight PaleMoon
  • Loading branch information
ronso0 committed Jul 25, 2023
1 parent cf22f6c commit d718ca3
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 12 deletions.
27 changes: 23 additions & 4 deletions res/skins/LateNight/style_palemoon.qss
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,22 @@ WSearchLineEdit {
border-color: #0c0c0c;
}

WTrackProperty,
WTrackProperty[selected="false"] {
background-color: transparent;
border: 0px solid transparent;
border-radius: 1px;
}
WTrackProperty:hover,
WTrackProperty:hover[selected="false"],
WTrackProperty:hover[selected="true"],
WTrackProperty[selected="true"],
#BpmTapContainer:hover,
#PlayPositionText:hover, #PlayPositionTextSmall:hover {
background-color: #151517;
border-radius: 1px;
}
WTrackProperty[selected="true"] {
border: 1px solid #888;
}

/* Disabled for now since the hover effect is stuck as soon as the
Expand Down Expand Up @@ -2526,12 +2538,14 @@ WLibraryTextBrowser,
WLibrarySidebar,
#SkinSettings,
WSearchLineEdit,
WTrackProperty QLineEdit,
WLibrary QLineEdit,
#spinBoxTransition,
#LibraryBPMSpinBox {
background-color: #0f0f0f;
}

WTrackProperty QLineEdit,
WTrackTableView,
WLibraryTextBrowser,
WLibrarySidebar {
Expand Down Expand Up @@ -2589,12 +2603,17 @@ WTrackTableView {

/* Table cell in edit mode */
WLibrary QLineEdit,
#LibraryBPMSpinBox {
#LibraryBPMSpinBox,
WTrackProperty QLineEdit {
color: #ddd;
selection-color: #000;
selection-background-color: #ccc;
border: 1px solid #2c454f;
}
border: 1px solid #257b82;
border-radius: 0px;
}
WTrackProperty QLineEdit {
background-color: #000;
}

/* Entire BPM cell */
/* Lock icon at the left */
Expand Down
147 changes: 139 additions & 8 deletions src/widget/wtrackproperty.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "widget/wtrackproperty.h"

#include <QApplication>
#include <QDebug>
#include <QStyleOption>
#include <QUrl>

#include "control/controlobject.h"
Expand All @@ -25,6 +27,10 @@ constexpr WTrackMenu::Features kTrackMenuFeatures =
WTrackMenu::Feature::UpdateReplayGainFromPregain |
WTrackMenu::Feature::FindOnWeb |
WTrackMenu::Feature::SelectInLibrary;

// Duration (ms) the 'selected' state is true after left click, i.e. the duration
// a second click would open the value editor
constexpr int selectedClickTimeout = 2000;
} // namespace

WTrackProperty::WTrackProperty(
Expand All @@ -35,6 +41,9 @@ WTrackProperty::WTrackProperty(
: WLabel(pParent),
m_group(group),
m_pConfig(pConfig),
m_pSelectedClickTimer(nullptr),
m_bSelected(false),
m_pEditor(nullptr),
m_pTrackMenu(make_parented<WTrackMenu>(
this, pConfig, pLibrary, kTrackMenuFeatures)) {
setAcceptDrops(true);
Expand All @@ -47,12 +56,14 @@ WTrackProperty::~WTrackProperty() {
void WTrackProperty::setup(const QDomNode& node, const SkinContext& context) {
WLabel::setup(node, context);

m_property = context.selectString(node, "Property");
QString property = context.selectString(node, "Property");

// Check if property with that name exists in Track class
if (Track::staticMetaObject.indexOfProperty(m_property.toUtf8().constData()) == -1) {
qWarning() << "WTrackProperty: Unknown track property:" << m_property;
if (Track::staticMetaObject.indexOfProperty(property.toUtf8().constData()) == -1) {
qWarning() << "WTrackProperty: Unknown track property:" << property;
return;
}
m_property = property;
}

void WTrackProperty::slotTrackLoaded(TrackPointer pTrack) {
Expand Down Expand Up @@ -84,24 +95,90 @@ void WTrackProperty::slotTrackChanged(TrackId trackId) {

void WTrackProperty::updateLabel() {
if (m_pCurrentTrack) {
QVariant property = m_pCurrentTrack->property(m_property.toUtf8().constData());
if (property.isValid() && property.canConvert<QString>()) {
setText(property.toString());
return;
}
QString txt = getPropertyStringFromTrack();
setText(txt);
return;
}
setText("");
}
const QString WTrackProperty::getPropertyStringFromTrack() const {
if (m_property.isEmpty()) {
return {};
}
VERIFY_OR_DEBUG_ASSERT(m_pCurrentTrack) {
return {};
}
QVariant property = m_pCurrentTrack->property(m_property.toUtf8().constData());
if (property.isValid() && property.canConvert<QString>()) {
return property.toString();
}
return {};
}

void WTrackProperty::mouseMoveEvent(QMouseEvent* event) {
if (event->buttons().testFlag(Qt::LeftButton) && m_pCurrentTrack) {
DragAndDropHelper::dragTrack(m_pCurrentTrack, this, m_group);
}
}

void WTrackProperty::mousePressEvent(QMouseEvent* event) {
if (!event->buttons().testFlag(Qt::LeftButton) || !m_pCurrentTrack) {
return;
}

// close any open editor
PropertyEditor* oed = qobject_cast<PropertyEditor*>(QApplication::focusWidget());
if (oed) {
oed->clearFocus();
}

if (!m_pSelectedClickTimer) {
// create & start the timer
m_pSelectedClickTimer = new QTimer(this);
m_pSelectedClickTimer->setSingleShot(true);
m_pSelectedClickTimer->setInterval(selectedClickTimeout);
m_pSelectedClickTimer->callOnTimeout(
this, &WTrackProperty::resetSelectedState);
} else if (m_pSelectedClickTimer->isActive()) {
m_pSelectedClickTimer->stop();
m_bSelected = false;
resetSelectedState();
// create the persistent editor, populate & connect
if (!m_pEditor) {
m_pEditor = make_parented<PropertyEditor>(this);
connect(m_pEditor,
// use custom signal. editingFinished() doesn't suit since it's
// also emitted weh pressing Esc (which should cancel editing)
&PropertyEditor::commitEditorData,
this,
&WTrackProperty::slotCommitEditorData);
}
m_pEditor->setFixedSize(size());
m_pEditor->setText(getPropertyStringFromTrack());
m_pEditor->selectAll();
m_pEditor->show();
m_pEditor->setFocus();
return;
}
// start timer
m_pSelectedClickTimer->start();
m_bSelected = true;
// focus this to unfocus any other open editor, i.e. hide & discard changes
// positive side effect: also removes focus from beatsize spinboxes
// TODO backport to 2.4
restyleAndRepaint();
}

void WTrackProperty::mouseDoubleClickEvent(QMouseEvent* event) {
Q_UNUSED(event);
if (m_pCurrentTrack) {
resetSelectedState();
if (m_pSelectedClickTimer && m_pSelectedClickTimer->isActive()) {
m_pSelectedClickTimer->stop();
// explicitly disconnect() queued signals? not crucial
// here since timeOut() just calls resetSelectedState()
}

m_pTrackMenu->loadTrack(m_pCurrentTrack, m_group);
m_pTrackMenu->slotShowDlgTrackInfo();
}
Expand All @@ -123,3 +200,57 @@ void WTrackProperty::contextMenuEvent(QContextMenuEvent* event) {
m_pTrackMenu->popup(event->globalPos());
}
}

void WTrackProperty::slotCommitEditorData(const QString& text) {
// use real track data instead of text() to be independent from display text
if (m_pCurrentTrack && text != getPropertyStringFromTrack()) {
const QVariant var(QVariant::fromValue(text));
m_pCurrentTrack->setProperty(
m_property.toUtf8().constData(),
var);
// Track::changed() will update label
}
}

void WTrackProperty::restyleAndRepaint() {
emit selectedStateChanged(isSelected());

style()->unpolish(this);
style()->polish(this);
// These calls don't always trigger the repaint, so call it explicitly.
repaint();
}

PropertyEditor::PropertyEditor(QWidget* pParent)
: QLineEdit(pParent) {
installEventFilter(this);
}

bool PropertyEditor::eventFilter(QObject* pObj, QEvent* pEvent) {
if (pEvent->type() == QEvent::KeyPress) {
// Esc will close & reset.
// Enter/Return confirms.
// Any other keypress is forwarded.
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(pEvent);
const int key = keyEvent->key();
switch (key) {
case Qt::Key_Escape:
hide();
return true;
case Qt::Key_Return:
case Qt::Key_Enter:
hide();
emit commitEditorData(text());
return true;
default:
break;
}
} else if (pEvent->type() == QEvent::FocusOut) {
// Close and commit if any other widget gets focus
if (isVisible()) {
hide();
emit commitEditorData(text());
}
}
return QLineEdit::eventFilter(pObj, pEvent);
}
35 changes: 35 additions & 0 deletions src/widget/wtrackproperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QDragEnterEvent>
#include <QDropEvent>
#include <QLineEdit>
#include <QMouseEvent>

#include "preferences/usersettings.h"
Expand All @@ -15,6 +16,18 @@
class Library;
class WTrackMenu;

class PropertyEditor : public QLineEdit {
Q_OBJECT
public:
PropertyEditor(QWidget* pParent);

protected:
bool eventFilter(QObject* pObj, QEvent* pEvent);

signals:
void commitEditorData(const QString& text);
};

class WTrackProperty : public WLabel, public TrackDropTarget {
Q_OBJECT
public:
Expand All @@ -24,12 +37,23 @@ class WTrackProperty : public WLabel, public TrackDropTarget {
Library* pLibrary,
const QString& group);
~WTrackProperty() override;
// Custom property to allow skins to style the 'selected' state when the
// widget awaits a second click to open the editor.
// It's reset automatically if no second click is registered within the
// specified interval.
// Usage in css: WTrackProperty[selected="true"/"false"] { /* styles */ }
Q_PROPERTY(bool selected READ isSelected NOTIFY selectedStateChanged);

bool isSelected() const {
return m_bSelected;
}

void setup(const QDomNode& node, const SkinContext& context) override;

signals:
void trackDropped(const QString& filename, const QString& group) override;
void cloneDeck(const QString& sourceGroup, const QString& targetGroup) override;
void selectedStateChanged(bool state);

public slots:
void slotTrackLoaded(TrackPointer pTrack);
Expand All @@ -40,19 +64,30 @@ public slots:

private slots:
void slotTrackChanged(TrackId);
void resetSelectedState() {
m_bSelected = false;
restyleAndRepaint();
}
void slotCommitEditorData(const QString& text);

private:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;

void updateLabel();
const QString getPropertyStringFromTrack() const;
void restyleAndRepaint();

const QString m_group;
const UserSettingsPointer m_pConfig;
TrackPointer m_pCurrentTrack;
QString m_property;
QTimer* m_pSelectedClickTimer;
bool m_bSelected;
parented_ptr<PropertyEditor> m_pEditor;

const parented_ptr<WTrackMenu> m_pTrackMenu;
};

0 comments on commit d718ca3

Please sign in to comment.