4566353fe5
* Fix build with Clang 10 * System depend code deduplication * Improve multi-layer style picker. Allows clicking anywhere on a stroke in another layer to pick it - not just the center. Fixes #2843. * Get actual pixel size instead of hard-coding to 1.0. * Rehide features * fix pegbar restoring * gui tweaks tweaks the gui, makes tpaneltitlebarbutton color states changable in stylesheets * fix styleeditor chandle mouse event allow handle to be grabbed properly without slipping Co-authored-by: Rodney <rodney.baker@gmail.com> Co-authored-by: Rozhuk Ivan <rozhuk.im@gmail.com> Co-authored-by: Martin van Zijl <martin.vanzijl@gmail.com> Co-authored-by: shun-iwasawa <shun.iwasawa@ghibli.jp> Co-authored-by: Kite <konero@users.noreply.github.com>
589 lines
18 KiB
C++
589 lines
18 KiB
C++
|
|
|
|
#include "pane.h"
|
|
|
|
// Tnz6 includes
|
|
#include "tapp.h"
|
|
#include "mainwindow.h"
|
|
#include "tenv.h"
|
|
#include "saveloadqsettings.h"
|
|
|
|
#include "toonzqt/gutil.h"
|
|
|
|
// TnzLib includes
|
|
#include "toonz/preferences.h"
|
|
#include "toonz/toonzfolders.h"
|
|
#include "toonz/tscenehandle.h"
|
|
|
|
// TnzCore includes
|
|
#include "tsystem.h"
|
|
|
|
// Qt includes
|
|
#include <QPainter>
|
|
#include <QStyleOptionDockWidget>
|
|
#include <QMouseEvent>
|
|
#include <QMainWindow>
|
|
#include <QSettings>
|
|
#include <QToolBar>
|
|
#include <QMap>
|
|
#include <QApplication>
|
|
#include <QFile>
|
|
#include <qdrawutil.h>
|
|
#include <assert.h>
|
|
#include <QDesktopWidget>
|
|
#include <QDialog>
|
|
#include <QLineEdit>
|
|
|
|
extern TEnv::StringVar EnvSafeAreaName;
|
|
|
|
//=============================================================================
|
|
// TPanel
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanel::TPanel(QWidget *parent, Qt::WindowFlags flags,
|
|
TDockWidget::Orientation orientation)
|
|
: TDockWidget(parent, flags)
|
|
, m_panelType("")
|
|
, m_isMaximizable(true)
|
|
, m_isMaximized(false)
|
|
, m_isActive(true)
|
|
, m_panelTitleBar(0)
|
|
, m_multipleInstancesAllowed(true) {
|
|
// setFeatures(QDockWidget::DockWidgetMovable |
|
|
// QDockWidget::DockWidgetFloatable);
|
|
// setFloating(false);
|
|
m_panelTitleBar = new TPanelTitleBar(this, orientation);
|
|
setTitleBarWidget(m_panelTitleBar);
|
|
// connect(m_panelTitleBar,SIGNAL(doubleClick()),this,SLOT(onDoubleClick()));
|
|
connect(m_panelTitleBar, SIGNAL(doubleClick(QMouseEvent *)), this,
|
|
SIGNAL(doubleClick(QMouseEvent *)));
|
|
connect(m_panelTitleBar, SIGNAL(closeButtonPressed()), this,
|
|
SLOT(onCloseButtonPressed()));
|
|
setOrientation(orientation);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanel::~TPanel() {
|
|
// On quitting, save the floating panel's geomtry and state in order to
|
|
// restore them when opening the floating panel next time
|
|
if (isFloating()) {
|
|
TFilePath savePath =
|
|
ToonzFolder::getMyModuleDir() + TFilePath("popups.ini");
|
|
QSettings settings(QString::fromStdWString(savePath.getWideString()),
|
|
QSettings::IniFormat);
|
|
settings.beginGroup("Panels");
|
|
settings.beginGroup(QString::fromStdString(m_panelType));
|
|
settings.setValue("geometry", geometry());
|
|
if (SaveLoadQSettings *persistent =
|
|
dynamic_cast<SaveLoadQSettings *>(widget()))
|
|
persistent->save(settings);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanel::setActive(bool value) {
|
|
m_isActive = value;
|
|
if (m_panelTitleBar) {
|
|
m_panelTitleBar->setIsActive(m_isActive);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanel::paintEvent(QPaintEvent *e) {
|
|
QPainter painter(this);
|
|
|
|
if (widget()) {
|
|
QRect dockRect = widget()->geometry();
|
|
|
|
dockRect.adjust(0, 0, -1, -1);
|
|
painter.fillRect(dockRect, m_bgcolor);
|
|
painter.setPen(Qt::black);
|
|
painter.drawRect(dockRect);
|
|
}
|
|
|
|
if (m_floating && !m_panelTitleBar->isVisible()) {
|
|
m_panelTitleBar->showTitleBar(true);
|
|
}
|
|
|
|
painter.end();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanel::onCloseButtonPressed() {
|
|
emit closeButtonPressed();
|
|
|
|
// Currently, Toonz panels that get closed indeed just remain hidden -
|
|
// ready to reappair if they are needed again. However, the user expects
|
|
// a new panel to be created - so we just reset the panel here.
|
|
// reset(); //Moved to panel invocation in floatingpanelcommand.cpp
|
|
|
|
// Also, remove widget from its dock layout control
|
|
if (parentLayout()) parentLayout()->removeWidget(this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*! activate the panel and set focus specified widget when mouse enters
|
|
*/
|
|
void TPanel::enterEvent(QEvent *event) {
|
|
// Only when Toonz application is active
|
|
QWidget *w = qApp->activeWindow();
|
|
// if (m_floating) {
|
|
// m_panelTitleBar->showTitleBar(true);
|
|
//}
|
|
if (w) {
|
|
// grab the focus, unless a line-edit is focused currently
|
|
bool shouldSetFocus = true;
|
|
|
|
QWidget *focusWidget = qApp->focusWidget();
|
|
if (focusWidget) {
|
|
QLineEdit *lineEdit = dynamic_cast<QLineEdit *>(focusWidget);
|
|
if (lineEdit) {
|
|
shouldSetFocus = false;
|
|
}
|
|
}
|
|
|
|
if (shouldSetFocus) {
|
|
widgetFocusOnEnter();
|
|
}
|
|
|
|
// Some panels (e.g. Viewer, StudioPalette, Palette, ColorModel) are
|
|
// activated when mouse enters. Viewer is activatable only when being
|
|
// docked.
|
|
// Active windows will NOT switch when the current active window is dialog.
|
|
if (qobject_cast<QDialog *>(w) == 0 && isActivatableOnEnter())
|
|
activateWindow();
|
|
event->accept();
|
|
} else
|
|
event->accept();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*! clear focus when mouse leaves
|
|
*/
|
|
void TPanel::leaveEvent(QEvent *event) { widgetClearFocusOnLeave(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*! load and restore previous geometry and state of the floating panel.
|
|
called from the function OpenFloatingPanel::getOrOpenFloatingPanel()
|
|
in floatingpanelcommand.cpp
|
|
*/
|
|
void TPanel::restoreFloatingPanelState() {
|
|
TFilePath savePath = ToonzFolder::getMyModuleDir() + TFilePath("popups.ini");
|
|
QSettings settings(QString::fromStdWString(savePath.getWideString()),
|
|
QSettings::IniFormat);
|
|
settings.beginGroup("Panels");
|
|
|
|
if (!settings.childGroups().contains(QString::fromStdString(m_panelType)))
|
|
return;
|
|
|
|
settings.beginGroup(QString::fromStdString(m_panelType));
|
|
|
|
QRect geom = settings.value("geometry", saveGeometry()).toRect();
|
|
// check if it can be visible in the current screen
|
|
if (!(geom & QApplication::desktop()->availableGeometry(this)).isEmpty())
|
|
setGeometry(geom);
|
|
// load optional settings
|
|
if (SaveLoadQSettings *persistent =
|
|
dynamic_cast<SaveLoadQSettings *>(widget()))
|
|
persistent->load(settings);
|
|
}
|
|
|
|
//=============================================================================
|
|
// TPanelTitleBarButton
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelTitleBarButton::TPanelTitleBarButton(QWidget *parent,
|
|
const QString &standardPixmapName)
|
|
: QWidget(parent)
|
|
, m_standardPixmap(standardPixmapName)
|
|
, m_standardPixmapName(standardPixmapName)
|
|
, m_rollover(false)
|
|
, m_pressed(false)
|
|
, m_buttonSet(0)
|
|
, m_id(0) {
|
|
setFixedSize(m_standardPixmap.size());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelTitleBarButton::TPanelTitleBarButton(QWidget *parent,
|
|
const QPixmap &standardPixmap)
|
|
: QWidget(parent)
|
|
, m_standardPixmap(standardPixmap)
|
|
, m_rollover(false)
|
|
, m_pressed(false)
|
|
, m_buttonSet(0)
|
|
, m_id(0) {
|
|
setFixedSize(m_standardPixmap.size() / m_standardPixmap.devicePixelRatio());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::setButtonSet(TPanelTitleBarButtonSet *buttonSet,
|
|
int id) {
|
|
m_buttonSet = buttonSet;
|
|
m_id = id;
|
|
m_buttonSet->add(this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::setPressed(bool pressed) {
|
|
if (pressed != m_pressed) {
|
|
m_pressed = pressed;
|
|
update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::paintEvent(QPaintEvent *event) {
|
|
QPainter painter(this);
|
|
|
|
// create color states for the button
|
|
QPixmap standard_pm(m_standardPixmap.size());
|
|
QPixmap rollover_pm(m_standardPixmap.size());
|
|
QPixmap pressed_pm(m_standardPixmap.size());
|
|
standard_pm.fill(Qt::transparent);
|
|
rollover_pm.fill(QColor(getRolloverColor()));
|
|
pressed_pm.fill(QColor(getPressedColor()));
|
|
|
|
// set unique colors if filename contains string
|
|
if (m_standardPixmapName.contains("freeze", Qt::CaseInsensitive)) {
|
|
pressed_pm.fill(QColor(getFreezeColor()));
|
|
}
|
|
if (m_standardPixmapName.contains("preview", Qt::CaseInsensitive)) {
|
|
pressed_pm.fill(QColor(getPreviewColor()));
|
|
}
|
|
|
|
// compose the state colors
|
|
painter.drawPixmap(
|
|
0, 0, m_pressed ? pressed_pm : m_rollover ? rollover_pm : standard_pm);
|
|
// compose the icon
|
|
painter.drawPixmap(0, 0, m_standardPixmap);
|
|
|
|
painter.end();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::mouseMoveEvent(QMouseEvent *event) {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::enterEvent(QEvent *) {
|
|
if (!m_rollover) {
|
|
m_rollover = true;
|
|
if (!m_pressed) update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::leaveEvent(QEvent *) {
|
|
if (m_rollover) {
|
|
m_rollover = false;
|
|
if (!m_pressed) update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButton::mousePressEvent(QMouseEvent *e) {
|
|
if (m_buttonSet) {
|
|
if (m_pressed) return;
|
|
m_buttonSet->select(this);
|
|
} else {
|
|
m_pressed = !m_pressed;
|
|
emit toggled(m_pressed);
|
|
update();
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// TPanelTitleBarButtonForSafeArea
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButtonForSafeArea::getSafeAreaNameList(
|
|
QList<QString> &nameList) {
|
|
TFilePath fp = TEnv::getConfigDir();
|
|
QString currentSafeAreaName = QString::fromStdString(EnvSafeAreaName);
|
|
|
|
std::string safeAreaFileName = "safearea.ini";
|
|
|
|
while (!TFileStatus(fp + safeAreaFileName).doesExist() && !fp.isRoot() &&
|
|
fp.getParentDir() != TFilePath())
|
|
fp = fp.getParentDir();
|
|
|
|
fp = fp + safeAreaFileName;
|
|
|
|
if (TFileStatus(fp).doesExist()) {
|
|
QSettings settings(toQString(fp), QSettings::IniFormat);
|
|
|
|
// find the current safearea name from the list
|
|
QStringList groups = settings.childGroups();
|
|
for (int g = 0; g < groups.size(); g++) {
|
|
settings.beginGroup(groups.at(g));
|
|
nameList.push_back(settings.value("name", "").toString());
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButtonForSafeArea::mousePressEvent(QMouseEvent *e) {
|
|
if (e->button() != Qt::RightButton) {
|
|
m_pressed = !m_pressed;
|
|
emit toggled(m_pressed);
|
|
update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButtonForSafeArea::contextMenuEvent(QContextMenuEvent *e) {
|
|
QMenu menu(this);
|
|
|
|
QList<QString> safeAreaNameList;
|
|
getSafeAreaNameList(safeAreaNameList);
|
|
for (int i = 0; i < safeAreaNameList.size(); i++) {
|
|
QAction *action = new QAction(safeAreaNameList.at(i), this);
|
|
action->setData(safeAreaNameList.at(i));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(onSetSafeArea()));
|
|
if (safeAreaNameList.at(i) == QString::fromStdString(EnvSafeAreaName)) {
|
|
action->setCheckable(true);
|
|
action->setChecked(true);
|
|
}
|
|
menu.addAction(action);
|
|
}
|
|
|
|
menu.exec(e->globalPos());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBarButtonForSafeArea::onSetSafeArea() {
|
|
QString safeAreaName = qobject_cast<QAction *>(sender())->data().toString();
|
|
// change safearea if the different one is selected
|
|
if (QString::fromStdString(EnvSafeAreaName) != safeAreaName) {
|
|
EnvSafeAreaName = safeAreaName.toStdString();
|
|
// emit sceneChanged without setting dirty flag
|
|
TApp::instance()->getCurrentScene()->notifySceneChanged(false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// TPanelTitleBarButtonSet
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelTitleBarButtonSet::TPanelTitleBarButtonSet() {}
|
|
|
|
TPanelTitleBarButtonSet::~TPanelTitleBarButtonSet() {}
|
|
|
|
void TPanelTitleBarButtonSet::add(TPanelTitleBarButton *button) {
|
|
m_buttons.push_back(button);
|
|
}
|
|
|
|
void TPanelTitleBarButtonSet::select(TPanelTitleBarButton *button) {
|
|
int i;
|
|
for (i = 0; i < (int)m_buttons.size(); i++)
|
|
m_buttons[i]->setPressed(button == m_buttons[i]);
|
|
emit selected(button->getId());
|
|
}
|
|
|
|
//=============================================================================
|
|
// PaneTitleBar
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelTitleBar::TPanelTitleBar(QWidget *parent,
|
|
TDockWidget::Orientation orientation)
|
|
: QFrame(parent), m_isActive(true), m_closeButtonHighlighted(false) {
|
|
setMouseTracking(true);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::setIsActive(bool value) {
|
|
if (m_isActive == value) return;
|
|
m_isActive = value;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QSize TPanelTitleBar::minimumSizeHint() const { return QSize(20, 18); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::paintEvent(QPaintEvent *) {
|
|
QPainter painter(this);
|
|
QRect rect = this->rect();
|
|
|
|
bool isPanelActive;
|
|
|
|
TPanel *dw = qobject_cast<TPanel *>(parentWidget());
|
|
Q_ASSERT(dw != 0);
|
|
// docked panel
|
|
if (!dw->isFloating()) {
|
|
isPanelActive = dw->widgetInThisPanelIsFocused();
|
|
qDrawBorderPixmap(&painter, rect, QMargins(3, 3, 3, 3),
|
|
(isPanelActive) ? m_activeBorderPm : m_borderPm);
|
|
}
|
|
// floating panel
|
|
else {
|
|
isPanelActive = isActiveWindow();
|
|
qDrawBorderPixmap(
|
|
&painter, rect, QMargins(3, 3, 3, 3),
|
|
(isPanelActive) ? m_floatActiveBorderPm : m_floatBorderPm);
|
|
}
|
|
|
|
if (dw->getOrientation() == TDockWidget::vertical) {
|
|
QString titleText = painter.fontMetrics().elidedText(
|
|
dw->windowTitle(), Qt::ElideRight, rect.width() - 50);
|
|
|
|
painter.setBrush(Qt::NoBrush);
|
|
painter.setPen(isPanelActive ? m_activeTitleColor : m_titleColor);
|
|
painter.drawText(QPointF(10, 15), titleText);
|
|
}
|
|
|
|
if (dw->isFloating()) {
|
|
const static QPixmap closeButtonPixmap(
|
|
svgToPixmap(":/Resources/pane_close.svg", QSize(18, 18)));
|
|
const static QPixmap closeButtonPixmapOver(
|
|
svgToPixmap(":/Resources/pane_close_rollover.svg", QSize(18, 18)));
|
|
|
|
QPoint closeButtonPos(rect.right() - 18, rect.top() + 1);
|
|
|
|
if (m_closeButtonHighlighted)
|
|
painter.drawPixmap(closeButtonPos, closeButtonPixmapOver);
|
|
else
|
|
painter.drawPixmap(closeButtonPos, closeButtonPixmap);
|
|
}
|
|
|
|
painter.end();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::mousePressEvent(QMouseEvent *event) {
|
|
TDockWidget *dw = static_cast<TDockWidget *>(parentWidget());
|
|
|
|
QPoint pos = event->pos();
|
|
|
|
if (dw->isFloating()) {
|
|
QRect rect = this->rect();
|
|
QRect closeButtonRect(rect.right() - 18, rect.top() + 1, 18, 18);
|
|
if (closeButtonRect.contains(pos) && dw->isFloating()) {
|
|
event->accept();
|
|
dw->hide();
|
|
m_closeButtonHighlighted = false;
|
|
emit closeButtonPressed();
|
|
return;
|
|
}
|
|
}
|
|
event->ignore();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::mouseMoveEvent(QMouseEvent *event) {
|
|
TDockWidget *dw = static_cast<TDockWidget *>(parentWidget());
|
|
|
|
if (dw->isFloating()) {
|
|
QPoint pos = event->pos();
|
|
QRect rect = this->rect();
|
|
QRect closeButtonRect(rect.right() - 18, rect.top() + 1, 18, 18);
|
|
|
|
if (closeButtonRect.contains(pos) && dw->isFloating())
|
|
m_closeButtonHighlighted = true;
|
|
else
|
|
m_closeButtonHighlighted = false;
|
|
}
|
|
|
|
update();
|
|
event->ignore();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::mouseDoubleClickEvent(QMouseEvent *me) {
|
|
emit doubleClick(me);
|
|
me->ignore();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::add(const QPoint &pos, QWidget *widget) {
|
|
m_buttons.push_back(std::make_pair(pos, widget));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TPanelTitleBar::resizeEvent(QResizeEvent *e) {
|
|
QWidget::resizeEvent(e);
|
|
int i;
|
|
for (i = 0; i < (int)m_buttons.size(); i++) {
|
|
QPoint p = m_buttons[i].first;
|
|
QWidget *w = m_buttons[i].second;
|
|
if (p.x() < 0) p.setX(p.x() + width());
|
|
w->move(p);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// TPanelFactory
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelFactory::TPanelFactory(QString panelType) : m_panelType(panelType) {
|
|
assert(tableInstance().count(panelType) == 0);
|
|
tableInstance()[m_panelType] = this;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanelFactory::~TPanelFactory() { tableInstance().remove(m_panelType); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QMap<QString, TPanelFactory *> &TPanelFactory::tableInstance() {
|
|
static QMap<QString, TPanelFactory *> table;
|
|
return table;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanel *TPanelFactory::createPanel(QWidget *parent, QString panelType) {
|
|
TPanel *panel = 0;
|
|
|
|
QMap<QString, TPanelFactory *>::iterator it = tableInstance().find(panelType);
|
|
if (it == tableInstance().end()) {
|
|
TPanel *panel = new TPanel(parent);
|
|
panel->setPanelType(panelType.toStdString());
|
|
return panel;
|
|
} else {
|
|
TPanelFactory *factory = it.value();
|
|
TPanel *panel = factory->createPanel(parent);
|
|
panel->setPanelType(panelType.toStdString());
|
|
return panel;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TPanel *TPanelFactory::createPanel(QWidget *parent) {
|
|
TPanel *panel = new TPanel(parent);
|
|
panel->setObjectName(getPanelType());
|
|
panel->setWindowTitle(getPanelType());
|
|
initialize(panel);
|
|
return panel;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|