Files
oak-editor/app/widget/slider/base/sliderladder.cpp
T
Mike-Solar bb40b4923e style: unify identifier naming per updated conventions
Automated with clang-tidy readability-identifier-naming (config added to
.clang-tidy) plus scripted passes, per the updated rules now documented
in CONTRIBUTING.md:

- types (class/struct/enum/alias/template params): PascalCase
- functions, variables, members: snake_case (incl. rational -> Rational)
- private/protected members: trailing underscore; static member
  variables likewise (instance_, available_themes_)
- constants and enum values: snake_case (kLinear -> k_linear,
  F32P -> f32p); ALL_CAPS reserved for macros
- macros: OAK_ prefix (OLIVE_ADD_TEST/OLIVE_ASSERT/OLIVE_CONFIG ->
  OAK_ADD_TEST/OAK_ASSERT/OAK_CONFIG, GL_PREAMBLE -> OAK_GL_PREAMBLE,
  include guards -> OAK_*)
- file names: all lowercase (Current/Plugin/OliveHost/OliveClip/
  OlivePluginInstance -> current/plugin/olivehost/oliveclip/
  oliveplugininstance)
- getters share the member name sans underscore, setters set_foo()
- Qt and third-party (OpenFX) virtual overrides and framework callbacks
  keep their original names (exempt in .clang-tidy)

Manual follow-ups required where automation could not reach:
- string-based QMetaObject/SIGNAL/SLOT references updated to renamed
  methods (AddTask, CreatedFile, DeleteSpecificFile, moveSelectionUp, ...)
- macro bodies referencing renamed methods (OLIVE_CONFIG,
  NODE_DEFAULT_DESTRUCTOR, MANAGEDDISPLAYWIDGET_*)
- self-shadowing locals renamed where signals/methods became same-named
  (size_changed, worker_count, selected_items, import param, filters)
- third_party OFX member/namespace usages restored (OFX::Host::*,
  _created, _clipPrefsDirty, createInstance, clearPersistentMessage)
- STL protocol aliases restored (const_iterator) with .clang-tidy
  ignore rules; qHash overloads restored

Full build and test suite pass: ctest 4/4, ~1960 gtest cases green.
2026-07-19 16:10:54 +08:00

351 lines
7.9 KiB
C++

/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "sliderladder.h"
#include <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QScreen>
#include <QtMath>
#include <QVBoxLayout>
#ifdef Q_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
#endif
#include "common/lerp.h"
#include "common/qtutils.h"
#include "config/config.h"
namespace olive
{
SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values,
QString width_hint, QWidget *parent)
: QFrame(parent, Qt::Popup)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
setFrameShape(QFrame::Box);
setLineWidth(1);
if (!OAK_CONFIG("UseSliderLadders").toBool()) {
nb_outer_values = 0;
}
for (int i = nb_outer_values - 1; i >= 0; i--) {
elements_.append(new SliderLadderElement(
qPow(10, i + 1) * drag_multiplier, width_hint));
}
// Create center entry
SliderLadderElement *start_element =
new SliderLadderElement(drag_multiplier, width_hint);
active_element_ = elements_.size();
start_element->set_highlighted(true);
elements_.append(start_element);
for (int i = 0; i < nb_outer_values; i++) {
elements_.append(new SliderLadderElement(
qPow(10, -i - 1) * drag_multiplier, width_hint));
}
foreach (SliderLadderElement *e, elements_) {
layout->addWidget(e);
}
if (elements_.size() == 1) {
elements_.first()->set_multiplier_visible(false);
}
drag_timer_.setInterval(10);
connect(&drag_timer_, &QTimer::timeout, this, &SliderLadder::timer_update);
screen_ = nullptr;
foreach (QScreen *screen, qApp->screens()) {
if (screen->geometry().contains(QCursor::pos())) {
screen_ = screen;
break;
}
}
if (using_ladders()) {
drag_start_x_ = -1;
wrap_count_ = 0;
} else {
#if defined(Q_OS_MAC)
CGAssociateMouseAndMouseCursorPosition(false);
CGDisplayHideCursor(kCGDirectMainDisplay);
CGGetLastMouseDelta(nullptr, nullptr);
#else
drag_start_x_ = QCursor::pos().x();
drag_start_y_ = QCursor::pos().y();
static_cast<QGuiApplication *>(QApplication::instance())
->setOverrideCursor(Qt::BlankCursor);
#endif
}
}
SliderLadder::~SliderLadder()
{
if (using_ladders()) {
if (wrap_count_ != 0) {
// If wrapped, restore cursor to ladder
QCursor::setPos(pos() + rect().center());
}
} else {
#if defined(Q_OS_MAC)
CGAssociateMouseAndMouseCursorPosition(true);
CGDisplayShowCursor(kCGDirectMainDisplay);
#else
static_cast<QGuiApplication *>(QApplication::instance())
->restoreOverrideCursor();
#endif
}
}
void SliderLadder::set_value(const QString &s)
{
foreach (SliderLadderElement *e, elements_) {
e->set_value(s);
}
}
void SliderLadder::start_listening_to_mouse_input()
{
QMetaObject::invokeMethod(&drag_timer_, "start", Qt::QueuedConnection);
}
void SliderLadder::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
this->close();
}
void SliderLadder::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event)
drag_timer_.stop();
emit released();
QFrame::closeEvent(event);
}
void SliderLadder::timer_update()
{
int ladder_left = this->x();
int ladder_right = this->x() + this->width() - 1;
int now_pos = QCursor::pos().x();
if (using_ladders()) {
bool is_under_mouse = (now_pos >= ladder_left &&
now_pos <= ladder_right && wrap_count_ == 0);
if (drag_start_x_ != -1 &&
(is_under_mouse ||
(drag_start_x_ < ladder_left && now_pos > ladder_right) ||
(drag_start_x_ > ladder_right && now_pos < ladder_left))) {
// We're ending a drag, try to return the value back to its beginning
int anchor;
if (drag_start_x_ < ladder_left) {
anchor = ladder_left;
} else {
anchor = ladder_right;
}
int makeup_value = anchor - drag_start_x_;
emit dragged_by_value(makeup_value,
elements_.at(active_element_)->get_multiplier());
drag_start_x_ = -1;
}
if (is_under_mouse) {
// Determine which element is currently active
for (int i = 0; i < elements_.size(); i++) {
if (elements_.at(i)->underMouse()) {
if (i != active_element_) {
elements_.at(active_element_)->set_highlighted(false);
active_element_ = i;
elements_.at(active_element_)->set_highlighted(true);
}
break;
}
}
} else {
if (drag_start_x_ == -1) {
// Drag is a new leave from the ladder, calculate origin
if (now_pos < ladder_left) {
drag_start_x_ = ladder_left;
} else {
drag_start_x_ = ladder_right;
}
}
emit dragged_by_value(now_pos - drag_start_x_,
elements_.at(active_element_)->get_multiplier());
// Determine if cursor is at desktop edge, if so wrap around to other side
if (screen_) {
int left = screen_->geometry().left();
int right = screen_->geometry().right();
int width = right - left;
if (now_pos <= left || now_pos >= right) {
int orig_now_pos = now_pos;
int orig_wrap = wrap_count_;
if (now_pos <= left) {
wrap_count_--;
now_pos += width;
} else {
wrap_count_++;
now_pos -= width;
}
QPoint p(now_pos, QCursor::pos().y());
QCursor::setPos(p);
if (QCursor::pos() != p) {
wrap_count_ = orig_wrap;
now_pos = orig_now_pos;
}
}
}
drag_start_x_ = now_pos;
}
} else {
int32_t x_mvmt, y_mvmt;
// Keep cursor in the same position
#if defined(Q_OS_MAC)
CGGetLastMouseDelta(&x_mvmt, &y_mvmt);
#else
QPoint current_pos = QCursor::pos();
x_mvmt = current_pos.x() - drag_start_x_;
y_mvmt = current_pos.y() - drag_start_y_;
QCursor::setPos(QPoint(drag_start_x_, drag_start_y_));
#endif
if (x_mvmt || y_mvmt) {
double multiplier = 1.0;
if (qApp->keyboardModifiers() & Qt::ControlModifier) {
multiplier *= 0.01;
}
if (qApp->keyboardModifiers() & Qt::ShiftModifier) {
multiplier *= 100.0;
}
emit dragged_by_value(x_mvmt + y_mvmt, multiplier);
}
}
}
bool SliderLadder::using_ladders() const
{
return elements_.size() > 1;
}
SliderLadderElement::SliderLadderElement(const double &multiplier,
QString width_hint, QWidget *parent)
: QWidget(parent)
, multiplier_(multiplier)
, highlighted_(false)
, multiplier_visible_(true)
{
QVBoxLayout *layout = new QVBoxLayout(this);
label_ = new QLabel();
label_->setAlignment(Qt::AlignCenter);
label_->setFixedWidth(
QtUtils::q_font_metrics_width(label_->fontMetrics(), width_hint));
layout->addWidget(label_);
QPalette p = palette();
QColor highlight_color = palette().text().color();
highlight_color.setAlpha(64);
p.setColor(QPalette::Highlight, highlight_color);
setPalette(p);
setAutoFillBackground(true);
update_label();
}
void SliderLadderElement::set_highlighted(bool e)
{
highlighted_ = e;
if (highlighted_) {
setBackgroundRole(QPalette::Highlight);
} else {
setBackgroundRole(QPalette::Window);
}
update_label();
}
void SliderLadderElement::set_value(const QString &value)
{
value_ = value;
update_label();
}
void SliderLadderElement::set_multiplier_visible(bool e)
{
multiplier_visible_ = e;
update_label();
}
void SliderLadderElement::update_label()
{
if (multiplier_visible_) {
QString val_text;
if (highlighted_) {
val_text = value_;
}
label_->setText(QStringLiteral("%1\n%2").arg(
QString::number(multiplier_), val_text));
} else {
label_->setText(value_);
}
}
}