diff --git a/app/app.pro b/app/app.pro index bbce4cb2f1..c109d73e22 100644 --- a/app/app.pro +++ b/app/app.pro @@ -90,6 +90,7 @@ HEADERS += \ src/generalpage.h \ src/shortcutspage.h \ src/timelinepage.h \ + src/toolboxwidget.h \ src/toolspage.h \ src/basedockwidget.h \ src/colorbox.h \ @@ -142,6 +143,7 @@ SOURCES += \ src/generalpage.cpp \ src/shortcutspage.cpp \ src/timelinepage.cpp \ + src/toolboxwidget.cpp \ src/toolspage.cpp \ src/basedockwidget.cpp \ src/colorbox.cpp \ diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 11fba978c9..30a42434e7 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -30,6 +30,7 @@ GNU General Public License for more details. #include "viewmanager.h" #include "layermanager.h" #include "scribblearea.h" +#include "toolmanager.h" #include "soundmanager.h" #include "playbackmanager.h" #include "colormanager.h" @@ -942,6 +943,12 @@ void ActionCommands::changeallKeyframeLineColor() } } + +void ActionCommands::resetAllTools() +{ + mEditor->tools()->resetAllTools(); +} + void ActionCommands::help() { QString url = "http://www.pencil2d.org/doc/"; diff --git a/app/src/actioncommands.h b/app/src/actioncommands.h index 6d8ac3e340..27a9b705c5 100644 --- a/app/src/actioncommands.h +++ b/app/src/actioncommands.h @@ -65,6 +65,8 @@ class ActionCommands : public QObject void GotoPrevKeyFrame(); Status addNewKey(); + void resetAllTools(); + /** Will insert a keyframe at the current position and push connected frames to the right */ Status insertKeyFrameAtCurrentPosition(); void removeKey(); diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index a6d4a34f21..ba05e3338c 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -170,7 +170,7 @@ void MainWindow2::createDockWidgets() mToolOptions = new ToolOptionWidget(this); mToolOptions->setObjectName("ToolOption"); - mToolBox = new ToolBoxWidget(this); + mToolBox = new ToolBoxDockWidget(this); mToolBox->setObjectName("ToolBox"); mDockWidgets @@ -409,19 +409,37 @@ void MainWindow2::createMenus() connect(ui->actionReverse_Frames_Order, &QAction::triggered, mCommands, &ActionCommands::reverseSelectedFrames); connect(ui->actionRemove_Frames, &QAction::triggered, mCommands, &ActionCommands::removeSelectedFrames); - //--- Tool Menu --- - connect(ui->actionMove, &QAction::triggered, mToolBox, &ToolBoxWidget::moveOn); - connect(ui->actionSelect, &QAction::triggered, mToolBox, &ToolBoxWidget::selectOn); - connect(ui->actionBrush, &QAction::triggered, mToolBox, &ToolBoxWidget::brushOn); - connect(ui->actionPolyline, &QAction::triggered, mToolBox, &ToolBoxWidget::polylineOn); - connect(ui->actionSmudge, &QAction::triggered, mToolBox, &ToolBoxWidget::smudgeOn); - connect(ui->actionPen, &QAction::triggered, mToolBox, &ToolBoxWidget::penOn); - connect(ui->actionHand, &QAction::triggered, mToolBox, &ToolBoxWidget::handOn); - connect(ui->actionPencil, &QAction::triggered, mToolBox, &ToolBoxWidget::pencilOn); - connect(ui->actionBucket, &QAction::triggered, mToolBox, &ToolBoxWidget::bucketOn); - connect(ui->actionEyedropper, &QAction::triggered, mToolBox, &ToolBoxWidget::eyedropperOn); - connect(ui->actionEraser, &QAction::triggered, mToolBox, &ToolBoxWidget::eraserOn); - connect(ui->actionResetToolsDefault, &QAction::triggered, mEditor->tools(), &ToolManager::resetAllTools); + + auto toolsActionGroup = new QActionGroup(this); + toolsActionGroup->setExclusive(true); + toolsActionGroup->addAction(ui->actionMove); + toolsActionGroup->addAction(ui->actionSelect); + toolsActionGroup->addAction(ui->actionBrush); + toolsActionGroup->addAction(ui->actionPolyline); + toolsActionGroup->addAction(ui->actionSmudge); + toolsActionGroup->addAction(ui->actionPen); + toolsActionGroup->addAction(ui->actionHand); + toolsActionGroup->addAction(ui->actionPencil); + toolsActionGroup->addAction(ui->actionBucket); + toolsActionGroup->addAction(ui->actionEyedropper); + toolsActionGroup->addAction(ui->actionEraser); + toolsActionGroup->addAction(ui->actionResetToolsDefault); + + connect(toolsActionGroup, &QActionGroup::triggered, this, [&](QAction* action) { + if (action == ui->actionMove) mToolBox->setActiveTool(MOVE); + else if (action == ui->actionSelect) mToolBox->setActiveTool(SELECT); + else if (action == ui->actionBrush) mToolBox->setActiveTool(BRUSH); + else if (action == ui->actionPolyline) mToolBox->setActiveTool(POLYLINE); + else if (action == ui->actionSmudge) mToolBox->setActiveTool(SMUDGE); + else if (action == ui->actionPen) mToolBox->setActiveTool(PEN); + else if (action == ui->actionHand) mToolBox->setActiveTool(HAND); + else if (action == ui->actionPencil) mToolBox->setActiveTool(PENCIL); + else if (action == ui->actionBucket) mToolBox->setActiveTool(BUCKET); + else if (action == ui->actionEyedropper) mToolBox->setActiveTool(EYEDROPPER); + else if (action == ui->actionEraser) mToolBox->setActiveTool(ERASER); + else if (action == ui->actionResetToolsDefault) mCommands->resetAllTools(); + else Q_UNREACHABLE(); + }); //--- Window Menu --- QMenu* winMenu = ui->menuWindows; @@ -1446,7 +1464,7 @@ void MainWindow2::makeConnections(Editor* editor, ColorInspector* colorInspector void MainWindow2::makeConnections(Editor* editor, ScribbleArea* scribbleArea) { connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::updateToolCursor); - connect(editor->tools(), &ToolManager::toolChanged, mToolBox, &ToolBoxWidget::onToolSetActive); + connect(editor->tools(), &ToolManager::toolChanged, mToolBox, &ToolBoxDockWidget::setActiveTool); connect(editor->tools(), &ToolManager::toolPropertyChanged, scribbleArea, &ScribbleArea::updateToolCursor); diff --git a/app/src/mainwindow2.h b/app/src/mainwindow2.h index d85d400a22..c65870258b 100644 --- a/app/src/mainwindow2.h +++ b/app/src/mainwindow2.h @@ -31,7 +31,7 @@ class ColorPaletteWidget; class OnionSkinWidget; class ToolOptionWidget; class TimeLine; -class ToolBoxWidget; +class ToolBoxDockWidget; class PreferencesDialog; class PreviewWidget; class ColorBox; @@ -157,7 +157,7 @@ private slots: ColorBox* mColorBox = nullptr; ColorPaletteWidget* mColorPalette = nullptr; ToolOptionWidget* mToolOptions = nullptr; - ToolBoxWidget* mToolBox = nullptr; + ToolBoxDockWidget* mToolBox = nullptr; RecentFileMenu* mRecentFileMenu = nullptr; PreferencesDialog* mPrefDialog = nullptr; //PreviewWidget* mPreview = nullptr; diff --git a/app/src/toolbox.cpp b/app/src/toolbox.cpp index 653172cff7..8d94cc2564 100644 --- a/app/src/toolbox.cpp +++ b/app/src/toolbox.cpp @@ -16,7 +16,6 @@ GNU General Public License for more details. */ #include "toolbox.h" -#include "ui_toolboxwidget.h" #include @@ -24,6 +23,9 @@ GNU General Public License for more details. #include #include #include +#include +#include +#include #include "flowlayout.h" #include "spinslider.h" @@ -32,294 +34,52 @@ GNU General Public License for more details. #include "layermanager.h" #include "pencilsettings.h" -// ---------------------------------------------------------------------------------- -QString GetToolTips(QString strCommandName) +ToolBoxDockWidget::ToolBoxDockWidget(QWidget* parent) : + BaseDockWidget(parent) { - strCommandName = QString("shortcuts/") + strCommandName; - QKeySequence keySequence(pencilSettings().value(strCommandName).toString()); - return QString("%1").arg(keySequence.toString()); // don't tr() this string. -} + mWidget = new ToolBoxWidget(this); -ToolBoxWidget::ToolBoxWidget(QWidget* parent) : - BaseDockWidget(parent), - ui(new Ui::ToolBoxWidget) -{ - ui->setupUi(this); + setWindowTitle(tr("Tools", "Window title of Tools")); } -ToolBoxWidget::~ToolBoxWidget() +ToolBoxDockWidget::~ToolBoxDockWidget() { QSettings settings(PENCIL2D, PENCIL2D); settings.setValue("ToolBoxGeom", this->saveGeometry()); - delete ui; } -void ToolBoxWidget::initUI() +void ToolBoxDockWidget::initUI() { -#ifdef __APPLE__ - // Only Mac needs this. ToolButton is naturally borderless on Win/Linux. - QString sStyle = - "QToolButton { border: 0px; }" - "QToolButton:pressed { border: 1px solid #ADADAD; border-radius: 2px; background-color: #D5D5D5; }" - "QToolButton:checked { border: 1px solid #ADADAD; border-radius: 2px; background-color: #D5D5D5; }"; - ui->pencilButton->setStyleSheet(sStyle); - ui->selectButton->setStyleSheet(sStyle); - ui->moveButton->setStyleSheet(sStyle); - ui->handButton->setStyleSheet(sStyle); - ui->penButton->setStyleSheet(sStyle); - ui->eraserButton->setStyleSheet(sStyle); - ui->polylineButton->setStyleSheet(sStyle); - ui->bucketButton->setStyleSheet(sStyle); - ui->brushButton->setStyleSheet(sStyle); - ui->eyedropperButton->setStyleSheet(sStyle); - ui->smudgeButton->setStyleSheet(sStyle); -#endif - - ui->pencilButton->setToolTip( tr( "Pencil Tool (%1): Sketch with pencil" ) - .arg( GetToolTips( CMD_TOOL_PENCIL ) ) ); - ui->selectButton->setToolTip( tr( "Select Tool (%1): Select an object" ) - .arg( GetToolTips( CMD_TOOL_SELECT ) ) ); - ui->moveButton->setToolTip( tr( "Move Tool (%1): Move an object" ) - .arg( GetToolTips( CMD_TOOL_MOVE ) ) ); - ui->handButton->setToolTip( tr( "Hand Tool (%1): Move the canvas" ) - .arg( GetToolTips( CMD_TOOL_HAND ) ) ); - ui->penButton->setToolTip( tr( "Pen Tool (%1): Sketch with pen" ) - .arg( GetToolTips( CMD_TOOL_PEN ) ) ); - ui->eraserButton->setToolTip( tr( "Eraser Tool (%1): Erase" ) - .arg( GetToolTips( CMD_TOOL_ERASER ) ) ); - ui->polylineButton->setToolTip( tr( "Polyline Tool (%1): Create line/curves" ) - .arg( GetToolTips( CMD_TOOL_POLYLINE ) ) ); - ui->bucketButton->setToolTip( tr( "Paint Bucket Tool (%1): Fill selected area with a color" ) - .arg( GetToolTips( CMD_TOOL_BUCKET ) ) ); - ui->brushButton->setToolTip( tr( "Brush Tool (%1): Paint smooth stroke with a brush" ) - .arg( GetToolTips( CMD_TOOL_BRUSH ) ) ); - ui->eyedropperButton->setToolTip( tr( "Eyedropper Tool (%1): " - "Set color from the stage
[ALT] for instant access" ) - .arg( GetToolTips( CMD_TOOL_EYEDROPPER ) ) ); - ui->smudgeButton->setToolTip( tr( "Smudge Tool (%1):
Edit polyline/curves
" - "Liquify bitmap pixels
(%1)+[Alt]: Smooth" ) - .arg( GetToolTips( CMD_TOOL_SMUDGE ) ) ); - - ui->pencilButton->setWhatsThis( tr( "Pencil Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_PENCIL ) ) ); - ui->selectButton->setWhatsThis( tr( "Select Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_SELECT ) ) ); - ui->moveButton->setWhatsThis( tr( "Move Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_MOVE ) ) ); - ui->handButton->setWhatsThis( tr( "Hand Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_HAND ) ) ); - ui->penButton->setWhatsThis( tr( "Pen Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_PEN ) ) ); - ui->eraserButton->setWhatsThis( tr( "Eraser Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_ERASER ) ) ); - ui->polylineButton->setWhatsThis( tr( "Polyline Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_POLYLINE ) ) ); - ui->bucketButton->setWhatsThis( tr( "Paint Bucket Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_BUCKET ) ) ); - ui->brushButton->setWhatsThis( tr( "Brush Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_BRUSH ) ) ); - ui->eyedropperButton->setWhatsThis( tr( "Eyedropper Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_EYEDROPPER ) ) ); - ui->smudgeButton->setWhatsThis( tr( "Smudge Tool (%1)" ) - .arg( GetToolTips( CMD_TOOL_SMUDGE ) ) ); - - connect(ui->pencilButton, &QToolButton::clicked, this, &ToolBoxWidget::pencilOn); - connect(ui->eraserButton, &QToolButton::clicked, this, &ToolBoxWidget::eraserOn); - connect(ui->selectButton, &QToolButton::clicked, this, &ToolBoxWidget::selectOn); - connect(ui->moveButton, &QToolButton::clicked, this, &ToolBoxWidget::moveOn); - connect(ui->penButton, &QToolButton::clicked, this, &ToolBoxWidget::penOn); - connect(ui->handButton, &QToolButton::clicked, this, &ToolBoxWidget::handOn); - connect(ui->polylineButton, &QToolButton::clicked, this, &ToolBoxWidget::polylineOn); - connect(ui->bucketButton, &QToolButton::clicked, this, &ToolBoxWidget::bucketOn); - connect(ui->eyedropperButton, &QToolButton::clicked, this, &ToolBoxWidget::eyedropperOn); - connect(ui->brushButton, &QToolButton::clicked, this, &ToolBoxWidget::brushOn); - connect(ui->smudgeButton, &QToolButton::clicked, this, &ToolBoxWidget::smudgeOn); - - connect(editor()->layers(), &LayerManager::currentLayerChanged, this, &ToolBoxWidget::onLayerDidChange); - - connect(this, &QDockWidget::dockLocationChanged, this, [=](Qt::DockWidgetArea area) { - if (area == Qt::DockWidgetArea::TopDockWidgetArea || area == Qt::BottomDockWidgetArea) { - const int minimumHeight = ui->scrollAreaWidgetContents_2->layout()->heightForWidth(width()); - ui->scrollArea->setMinimumHeight(minimumHeight); - setMinimumHeight(minimumHeight); - } else { - ui->scrollArea->setMinimumHeight(0); // Default value - // Don't set own minimum height and let Qt come up with a sensible value - } - }); + mWidget->setEditor(editor()); + mWidget->initUI(); - FlowLayout* flowlayout = new FlowLayout(3,3,3); + setWidget(mWidget); + setContentsMargins(0,0,0,0); - flowlayout->addWidget(ui->pencilButton); - flowlayout->addWidget(ui->eraserButton); - flowlayout->addWidget(ui->selectButton); - flowlayout->addWidget(ui->moveButton); - flowlayout->addWidget(ui->penButton); - flowlayout->addWidget(ui->handButton); - flowlayout->addWidget(ui->polylineButton); - flowlayout->addWidget(ui->bucketButton); - flowlayout->addWidget(ui->eyedropperButton); - flowlayout->addWidget(ui->brushButton); - flowlayout->addWidget(ui->smudgeButton); - - delete ui->scrollAreaWidgetContents_2->layout(); - ui->scrollAreaWidgetContents_2->setLayout(flowlayout); - ui->scrollAreaWidgetContents_2->setContentsMargins(0,0,0,0); + connect(editor()->layers(), &LayerManager::currentLayerChanged, this, &ToolBoxDockWidget::onLayerDidChange); QSettings settings(PENCIL2D, PENCIL2D); restoreGeometry(settings.value("ToolBoxGeom").toByteArray()); -} - -void ToolBoxWidget::updateUI() -{ -} - -void ToolBoxWidget::resizeEvent(QResizeEvent* event) -{ - QDockWidget::resizeEvent(event); - const int minimumHeight = ui->scrollArea->minimumHeight(); - if (minimumHeight <= 0) { return; } - setMinimumHeight(minimumHeight); + // Important to set the proper minimumSize; + setMinimumSize(mWidget->minimumSize()); } -void ToolBoxWidget::onToolSetActive(ToolType toolType) +void ToolBoxDockWidget::setActiveTool(ToolType type) { - deselectAllTools(); - switch (toolType) { - case ToolType::BRUSH: - ui->brushButton->setChecked(true); - break; - case ToolType::PEN: - ui->penButton->setChecked(true); - break; - case ToolType::PENCIL: - ui->pencilButton->setChecked(true); - break; - case ToolType::SELECT: - ui->selectButton->setChecked(true); - break; - case ToolType::HAND: - ui->handButton->setChecked(true); - break; - case ToolType::MOVE: - case ToolType::CAMERA: - ui->moveButton->setChecked(true); - break; - case ToolType::ERASER: - ui->eraserButton->setChecked(true); - break; - case ToolType::POLYLINE: - ui->polylineButton->setChecked(true); - break; - case ToolType::SMUDGE: - ui->smudgeButton->setChecked(true); - break; - case ToolType::BUCKET: - ui->bucketButton->setChecked(true); - break; - case ToolType::EYEDROPPER: - ui->eyedropperButton->setChecked(true); - break; - default: - break; - } -} - -void ToolBoxWidget::pencilOn() -{ - toolOn(PENCIL, ui->pencilButton); -} - -void ToolBoxWidget::eraserOn() -{ - toolOn(ERASER, ui->eraserButton); -} - -void ToolBoxWidget::selectOn() -{ - toolOn(SELECT, ui->selectButton); -} - -void ToolBoxWidget::moveOn() -{ - if (editor()->layers()->currentLayer()->type() == Layer::CAMERA) { - toolOn(CAMERA, ui->moveButton); - } else { - toolOn(MOVE, ui->moveButton); - } + mWidget->setActiveTool(type); } -void ToolBoxWidget::penOn() +void ToolBoxDockWidget::updateUI() { - toolOn(PEN, ui->penButton); -} - -void ToolBoxWidget::handOn() -{ - toolOn(HAND, ui->handButton); -} - -void ToolBoxWidget::polylineOn() -{ - toolOn(POLYLINE, ui->polylineButton); -} - -void ToolBoxWidget::bucketOn() -{ - toolOn(BUCKET, ui->bucketButton); -} - -void ToolBoxWidget::eyedropperOn() -{ - toolOn(EYEDROPPER, ui->eyedropperButton); -} - -void ToolBoxWidget::brushOn() -{ - toolOn(BRUSH, ui->brushButton); -} - -void ToolBoxWidget::smudgeOn() -{ - toolOn(SMUDGE, ui->smudgeButton); -} - -void ToolBoxWidget::deselectAllTools() -{ - ui->pencilButton->setChecked(false); - ui->eraserButton->setChecked(false); - ui->selectButton->setChecked(false); - ui->moveButton->setChecked(false); - ui->handButton->setChecked(false); - ui->penButton->setChecked(false); - ui->polylineButton->setChecked(false); - ui->bucketButton->setChecked(false); - ui->eyedropperButton->setChecked(false); - ui->brushButton->setChecked(false); - ui->smudgeButton->setChecked(false); -} - -void ToolBoxWidget::toolOn(ToolType toolType, QToolButton* toolButton) -{ - if (editor()->tools()->currentTool()->type() == toolType) { - // Prevent un-checking the current tool and do nothing - toolButton->setChecked(true); - return; - } - if (!editor()->tools()->leavingThisTool()) - { - toolButton->setChecked(false); - return; - } - editor()->tools()->setCurrentTool(toolType); + mWidget->updateUI(); } -void ToolBoxWidget::onLayerDidChange(int) +void ToolBoxDockWidget::onLayerDidChange(int) { BaseTool* currentTool = editor()->tools()->currentTool(); if (currentTool->type() == MOVE || currentTool->type() == CAMERA) { - moveOn(); + mWidget->moveOn(); } } diff --git a/app/src/toolbox.h b/app/src/toolbox.h index 617d9b07a1..b312397455 100644 --- a/app/src/toolbox.h +++ b/app/src/toolbox.h @@ -14,12 +14,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#ifndef TOOLBOXWIDGET_H -#define TOOLBOXWIDGET_H +#ifndef TOOLBOXDOCKWIDGET_H +#define TOOLBOXDOCKWIDGET_H #include "pencildef.h" #include "basedockwidget.h" +#include "toolboxwidget.h" + class QToolButton; class QGridLayout; class QIcon; @@ -27,45 +29,25 @@ class SpinSlider; class DisplayOptionWidget; class ToolOptionWidget; class Editor; +class FlowLayout; -namespace Ui { -class ToolBoxWidget; -} - -class ToolBoxWidget : public BaseDockWidget +class ToolBoxDockWidget : public BaseDockWidget { Q_OBJECT public: - ToolBoxWidget(QWidget* parent); - ~ToolBoxWidget() override; + ToolBoxDockWidget(QWidget* parent); + ~ToolBoxDockWidget() override; void initUI() override; void updateUI() override; -public slots: - void onToolSetActive(ToolType toolType); - void onLayerDidChange(int index); - void pencilOn(); - void eraserOn(); - void selectOn(); - void moveOn(); - void penOn(); - void handOn(); - void polylineOn(); - void bucketOn(); - void eyedropperOn(); - void brushOn(); - void smudgeOn(); - -protected: - void resizeEvent(QResizeEvent* event) override; + void setActiveTool(ToolType type); private: - void deselectAllTools(); - void toolOn(ToolType toolType, QToolButton* toolButton); + void onLayerDidChange(int); - Ui::ToolBoxWidget* ui = nullptr; + ToolBoxWidget* mWidget = nullptr; }; #endif diff --git a/app/src/toolboxwidget.cpp b/app/src/toolboxwidget.cpp new file mode 100644 index 0000000000..292c11a772 --- /dev/null +++ b/app/src/toolboxwidget.cpp @@ -0,0 +1,335 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang +Copyright (C) 2024-2099 Oliver S. Larsen + +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; version 2 of the License. + +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. + +*/ +#include "toolboxwidget.h" +#include "ui_toolboxwidget.h" + +#include +#include +#include +#include + +#include "layermanager.h" +#include "toolmanager.h" +#include "editor.h" +#include "pencilsettings.h" + +// ---------------------------------------------------------------------------------- +QString GetToolTips(QString strCommandName) +{ + strCommandName = QString("shortcuts/") + strCommandName; + QKeySequence keySequence(pencilSettings().value(strCommandName).toString()); + return QString("%1").arg(keySequence.toString()); // don't tr() this string. +} + +ToolBoxWidget::ToolBoxWidget(QWidget* parent) + : QWidget(parent), ui(new Ui::ToolBoxWidget) +{ + ui->setupUi(this); +} + +ToolBoxWidget::~ToolBoxWidget() +{ + delete ui; +} + +void ToolBoxWidget::initUI() +{ + +#ifdef __APPLE__ + // Only Mac needs this. ToolButton is naturally borderless on Win/Linux. + QString sStyle = + "QToolButton { border: 0px; }" + "QToolButton:pressed { border: 1px solid #ADADAD; border-radius: 2px; background-color: #D5D5D5; }" + "QToolButton:checked { border: 1px solid #ADADAD; border-radius: 2px; background-color: #D5D5D5; }"; + ui->pencilButton->setStyleSheet(sStyle); + ui->selectButton->setStyleSheet(sStyle); + ui->moveButton->setStyleSheet(sStyle); + ui->handButton->setStyleSheet(sStyle); + ui->penButton->setStyleSheet(sStyle); + ui->eraserButton->setStyleSheet(sStyle); + ui->polylineButton->setStyleSheet(sStyle); + ui->bucketButton->setStyleSheet(sStyle); + ui->brushButton->setStyleSheet(sStyle); + ui->eyedropperButton->setStyleSheet(sStyle); + ui->smudgeButton->setStyleSheet(sStyle); +#endif + + ui->pencilButton->setToolTip( tr( "Pencil Tool (%1): Sketch with pencil" ) + .arg( GetToolTips( CMD_TOOL_PENCIL ) ) ); + ui->selectButton->setToolTip( tr( "Select Tool (%1): Select an object" ) + .arg( GetToolTips( CMD_TOOL_SELECT ) ) ); + ui->moveButton->setToolTip( tr( "Move Tool (%1): Move an object" ) + .arg( GetToolTips( CMD_TOOL_MOVE ) ) ); + ui->handButton->setToolTip( tr( "Hand Tool (%1): Move the canvas" ) + .arg( GetToolTips( CMD_TOOL_HAND ) ) ); + ui->penButton->setToolTip( tr( "Pen Tool (%1): Sketch with pen" ) + .arg( GetToolTips( CMD_TOOL_PEN ) ) ); + ui->eraserButton->setToolTip( tr( "Eraser Tool (%1): Erase" ) + .arg( GetToolTips( CMD_TOOL_ERASER ) ) ); + ui->polylineButton->setToolTip( tr( "Polyline Tool (%1): Create line/curves" ) + .arg( GetToolTips( CMD_TOOL_POLYLINE ) ) ); + ui->bucketButton->setToolTip( tr( "Paint Bucket Tool (%1): Fill selected area with a color" ) + .arg( GetToolTips( CMD_TOOL_BUCKET ) ) ); + ui->brushButton->setToolTip( tr( "Brush Tool (%1): Paint smooth stroke with a brush" ) + .arg( GetToolTips( CMD_TOOL_BRUSH ) ) ); + ui->eyedropperButton->setToolTip( tr( "Eyedropper Tool (%1): " + "Set color from the stage
[ALT] for instant access" ) + .arg( GetToolTips( CMD_TOOL_EYEDROPPER ) ) ); + ui->smudgeButton->setToolTip( tr( "Smudge Tool (%1):
Edit polyline/curves
" + "Liquify bitmap pixels
(%1)+[Alt]: Smooth" ) + .arg( GetToolTips( CMD_TOOL_SMUDGE ) ) ); + + ui->pencilButton->setWhatsThis( tr( "Pencil Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_PENCIL ) ) ); + ui->selectButton->setWhatsThis( tr( "Select Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_SELECT ) ) ); + ui->moveButton->setWhatsThis( tr( "Move Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_MOVE ) ) ); + ui->handButton->setWhatsThis( tr( "Hand Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_HAND ) ) ); + ui->penButton->setWhatsThis( tr( "Pen Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_PEN ) ) ); + ui->eraserButton->setWhatsThis( tr( "Eraser Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_ERASER ) ) ); + ui->polylineButton->setWhatsThis( tr( "Polyline Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_POLYLINE ) ) ); + ui->bucketButton->setWhatsThis( tr( "Paint Bucket Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_BUCKET ) ) ); + ui->brushButton->setWhatsThis( tr( "Brush Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_BRUSH ) ) ); + ui->eyedropperButton->setWhatsThis( tr( "Eyedropper Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_EYEDROPPER ) ) ); + ui->smudgeButton->setWhatsThis( tr( "Smudge Tool (%1)" ) + .arg( GetToolTips( CMD_TOOL_SMUDGE ) ) ); + + connect(ui->pencilButton, &QToolButton::clicked, this, &ToolBoxWidget::pencilOn); + connect(ui->eraserButton, &QToolButton::clicked, this, &ToolBoxWidget::eraserOn); + connect(ui->selectButton, &QToolButton::clicked, this, &ToolBoxWidget::selectOn); + connect(ui->moveButton, &QToolButton::clicked, this, &ToolBoxWidget::moveOn); + connect(ui->penButton, &QToolButton::clicked, this, &ToolBoxWidget::penOn); + connect(ui->handButton, &QToolButton::clicked, this, &ToolBoxWidget::handOn); + connect(ui->polylineButton, &QToolButton::clicked, this, &ToolBoxWidget::polylineOn); + connect(ui->bucketButton, &QToolButton::clicked, this, &ToolBoxWidget::bucketOn); + connect(ui->eyedropperButton, &QToolButton::clicked, this, &ToolBoxWidget::eyedropperOn); + connect(ui->brushButton, &QToolButton::clicked, this, &ToolBoxWidget::brushOn); + connect(ui->smudgeButton, &QToolButton::clicked, this, &ToolBoxWidget::smudgeOn); + + mFlowlayout = new ToolBoxLayout(nullptr, 3,3,3); + + mFlowlayout->addWidget(ui->pencilButton); + mFlowlayout->addWidget(ui->eraserButton); + mFlowlayout->addWidget(ui->selectButton); + mFlowlayout->addWidget(ui->moveButton); + mFlowlayout->addWidget(ui->penButton); + mFlowlayout->addWidget(ui->handButton); + mFlowlayout->addWidget(ui->polylineButton); + mFlowlayout->addWidget(ui->bucketButton); + mFlowlayout->addWidget(ui->eyedropperButton); + mFlowlayout->addWidget(ui->brushButton); + mFlowlayout->addWidget(ui->smudgeButton); + + delete ui->scrollAreaWidgetContents_2->layout(); + ui->scrollAreaWidgetContents_2->setLayout(mFlowlayout); + + // Important to set the proper minimumSize; + ui->scrollArea->setMinimumSize(QSize(1,1)); + setMinimumSize(mFlowlayout->minimumSize()); + + QButtonGroup* buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(ui->pencilButton); + buttonGroup->addButton(ui->eraserButton); + buttonGroup->addButton(ui->selectButton); + buttonGroup->addButton(ui->moveButton); + buttonGroup->addButton(ui->penButton); + buttonGroup->addButton(ui->handButton); + buttonGroup->addButton(ui->polylineButton); + buttonGroup->addButton(ui->bucketButton); + buttonGroup->addButton(ui->eyedropperButton); + buttonGroup->addButton(ui->brushButton); + buttonGroup->addButton(ui->smudgeButton); +} + +int ToolBoxWidget::getMinHeightForWidth(int width) const +{ + return mFlowlayout->heightForWidth(width); +} + +QSize ToolBoxWidget::sizeHint() const +{ + return minimumSizeHint(); +} + +QSize ToolBoxWidget::minimumSizeHint() const +{ + int minWidth = mFlowlayout->minimumSize().width(); + return QSize(minWidth, getMinHeightForWidth(width())); +} + +void ToolBoxWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + + updateLayoutAlignment(); +} + +void ToolBoxWidget::updateLayoutAlignment() +{ + mFlowlayout->invalidate(); + if (mFlowlayout->rows() > 1) { + mFlowlayout->setAlignment(Qt::AlignJustify); + } else { + mFlowlayout->setAlignment(Qt::AlignHCenter); + } + + mFlowlayout->activate(); +} + +void ToolBoxWidget::updateUI() +{ +} + +void ToolBoxWidget::setActiveTool(ToolType toolType) +{ + switch (toolType) { + case ToolType::BRUSH: + brushOn(); + break; + case ToolType::PEN: + penOn(); + break; + case ToolType::PENCIL: + pencilOn(); + break; + case ToolType::SELECT: + selectOn(); + break; + case ToolType::HAND: + handOn(); + break; + case ToolType::MOVE: + case ToolType::CAMERA: + moveOn(); + break; + case ToolType::ERASER: + eraserOn(); + break; + case ToolType::POLYLINE: + polylineOn(); + break; + case ToolType::SMUDGE: + smudgeOn(); + break; + case ToolType::BUCKET: + bucketOn(); + break; + case ToolType::EYEDROPPER: + eyedropperOn(); + break; + default: + break; + } +} + +void ToolBoxWidget::pencilOn() +{ + toolOn(PENCIL, ui->pencilButton); +} + +void ToolBoxWidget::eraserOn() +{ + toolOn(ERASER, ui->eraserButton); +} + +void ToolBoxWidget::selectOn() +{ + toolOn(SELECT, ui->selectButton); +} + +void ToolBoxWidget::moveOn() +{ + if (mEditor->layers()->currentLayer()->type() == Layer::CAMERA) { + toolOn(CAMERA, ui->moveButton); + } else { + toolOn(MOVE, ui->moveButton); + } +} + +void ToolBoxWidget::penOn() +{ + toolOn(PEN, ui->penButton); +} + +void ToolBoxWidget::handOn() +{ + toolOn(HAND, ui->handButton); +} + +void ToolBoxWidget::polylineOn() +{ + toolOn(POLYLINE, ui->polylineButton); +} + +void ToolBoxWidget::bucketOn() +{ + toolOn(BUCKET, ui->bucketButton); +} + +void ToolBoxWidget::eyedropperOn() +{ + toolOn(EYEDROPPER, ui->eyedropperButton); +} + +void ToolBoxWidget::brushOn() +{ + toolOn(BRUSH, ui->brushButton); +} + +void ToolBoxWidget::smudgeOn() +{ + toolOn(SMUDGE, ui->smudgeButton); +} + +void ToolBoxWidget::deselectAllTools() +{ + ui->pencilButton->setChecked(false); + ui->eraserButton->setChecked(false); + ui->selectButton->setChecked(false); + ui->moveButton->setChecked(false); + ui->handButton->setChecked(false); + ui->penButton->setChecked(false); + ui->polylineButton->setChecked(false); + ui->bucketButton->setChecked(false); + ui->eyedropperButton->setChecked(false); + ui->brushButton->setChecked(false); + ui->smudgeButton->setChecked(false); +} + +void ToolBoxWidget::toolOn(ToolType toolType, QToolButton* toolButton) +{ + if (mEditor->tools()->currentTool()->type() == toolType) { + // Prevent un-checking the current tool and do nothing + toolButton->setChecked(true); + return; + } + if (!mEditor->tools()->leavingThisTool()) + { + toolButton->setChecked(false); + return; + } + mEditor->tools()->setCurrentTool(toolType); +} diff --git a/app/src/toolboxwidget.h b/app/src/toolboxwidget.h new file mode 100644 index 0000000000..9a5d86819d --- /dev/null +++ b/app/src/toolboxwidget.h @@ -0,0 +1,80 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang +Copyright (C) 2024-2099 Oliver S. Larsen + +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; version 2 of the License. + +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. + +*/ +#ifndef TOOLBOXWIDGET_H +#define TOOLBOXWIDGET_H + +#include +#include +#include + +#include "flowlayout.h" +#include "pencildef.h" +#include "toolboxlayout.h" + +class Editor; + +namespace Ui { +class ToolBoxWidget; +} + +class ToolBoxWidget : public QWidget +{ + Q_OBJECT +public: + ToolBoxWidget(QWidget* parent = nullptr); + ~ToolBoxWidget() override; + + void setEditor(Editor* editor) { mEditor = editor; } + void initUI(); + void updateUI(); + +public slots: + void setActiveTool(ToolType toolType); + +public: + + void pencilOn(); + void eraserOn(); + void selectOn(); + void moveOn(); + void penOn(); + void handOn(); + void polylineOn(); + void bucketOn(); + void eyedropperOn(); + void brushOn(); + void smudgeOn(); + + void updateLayoutAlignment(); + void deselectAllTools(); + void toolOn(ToolType toolType, QToolButton* toolButton); + +protected: + int getMinHeightForWidth(int width) const; + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + void resizeEvent(QResizeEvent* event) override; + +private: + FlowLayout* mFlowlayout = nullptr; + + Ui::ToolBoxWidget* ui = nullptr; + Editor* mEditor = nullptr; +}; + +#endif // TOOLBOXWIDGET_H diff --git a/app/src/tooloptionwidget.cpp b/app/src/tooloptionwidget.cpp index e435fabee8..efa434a338 100644 --- a/app/src/tooloptionwidget.cpp +++ b/app/src/tooloptionwidget.cpp @@ -52,6 +52,9 @@ void ToolOptionWidget::initUI() ui->horizontalLayout_2->addWidget(mBucketOptionsWidget); ui->horizontalLayout_2->addWidget(mCameraOptionsWidget); + mBucketOptionsWidget->setHidden(true); + mCameraOptionsWidget->setHidden(true); + QSettings settings(PENCIL2D, PENCIL2D); ui->sizeSlider->init(tr("Width"), SpinSlider::EXPONENT, SpinSlider::INTEGER, StrokeTool::WIDTH_MIN, StrokeTool::WIDTH_MAX); diff --git a/app/ui/toolboxwidget.ui b/app/ui/toolboxwidget.ui index a2449d45f8..4c63c548ca 100644 --- a/app/ui/toolboxwidget.ui +++ b/app/ui/toolboxwidget.ui @@ -1,7 +1,7 @@ ToolBoxWidget - + 0 @@ -19,426 +19,424 @@ Tools - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 1 - - - true - - - - - 0 - 0 - 32 - 477 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 + + QFrame::Shadow::Plain + + + 1 + + + true + + + + + 0 + 0 + 32 + 461 + - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-pencil.svg:/icons/themes/playful/tools/tool-pencil.svg - - - - 22 - 22 - - - - true - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-eraser.svg:/icons/themes/playful/tools/tool-eraser.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-select.svg:/icons/themes/playful/tools/tool-select.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-move.svg:/icons/themes/playful/tools/tool-move.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-pen.svg:/icons/themes/playful/tools/tool-pen.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-hand.svg:/icons/themes/playful/tools/tool-hand.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-polyline.svg:/icons/themes/playful/tools/tool-polyline.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-bucket.svg:/icons/themes/playful/tools/tool-bucket.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-eyedropper.svg:/icons/themes/playful/tools/tool-eyedropper.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - :/icons/themes/playful/tools/tool-brush.svg:/icons/themes/playful/tools/tool-brush.svg - - - - 22 - 22 - - - - true - - - true - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - Smudge - - - - :/icons/themes/playful/tools/tool-smudge.svg:/icons/themes/playful/tools/tool-smudge.svg - - - - 22 - 22 - - - - true - - - true - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-pencil.svg:/icons/themes/playful/tools/tool-pencil.svg + + + + 22 + 22 + + + + true + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-eraser.svg:/icons/themes/playful/tools/tool-eraser.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-select.svg:/icons/themes/playful/tools/tool-select.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-move.svg:/icons/themes/playful/tools/tool-move.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-pen.svg:/icons/themes/playful/tools/tool-pen.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-hand.svg:/icons/themes/playful/tools/tool-hand.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-polyline.svg:/icons/themes/playful/tools/tool-polyline.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-bucket.svg:/icons/themes/playful/tools/tool-bucket.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-eyedropper.svg:/icons/themes/playful/tools/tool-eyedropper.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + :/icons/themes/playful/tools/tool-brush.svg:/icons/themes/playful/tools/tool-brush.svg + + + + 22 + 22 + + + + true + + + true + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + Smudge + + + + :/icons/themes/playful/tools/tool-smudge.svg:/icons/themes/playful/tools/tool-smudge.svg + + + + 22 + 22 + + + + true + + + true + + + + + - - - - + + diff --git a/core_lib/core_lib.pro b/core_lib/core_lib.pro index 9df20cb015..a7248881c1 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -44,6 +44,7 @@ HEADERS += \ src/interface/recentfilemenu.h \ src/interface/scribblearea.h \ src/interface/backgroundwidget.h \ + src/interface/toolboxlayout.h \ src/interface/undoredocommand.h \ src/managers/basemanager.h \ src/managers/overlaymanager.h \ @@ -135,6 +136,7 @@ SOURCES += src/graphics/bitmap/bitmapimage.cpp \ src/interface/recentfilemenu.cpp \ src/interface/scribblearea.cpp \ src/interface/backgroundwidget.cpp \ + src/interface/toolboxlayout.cpp \ src/interface/undoredocommand.cpp \ src/managers/basemanager.cpp \ src/managers/overlaymanager.cpp \ diff --git a/core_lib/src/interface/flowlayout.cpp b/core_lib/src/interface/flowlayout.cpp index 032f065c81..d98a7f8d67 100644 --- a/core_lib/src/interface/flowlayout.cpp +++ b/core_lib/src/interface/flowlayout.cpp @@ -47,10 +47,28 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +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; version 2 of the License. + +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. + +*/ #include #include #include +#include +#include #include "flowlayout.h" @@ -61,10 +79,8 @@ FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) } FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) - : m_hSpace(hSpacing), m_vSpace(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} + : FlowLayout(nullptr, margin, hSpacing, vSpacing) +{} FlowLayout::~FlowLayout() { @@ -114,26 +130,25 @@ QLayoutItem *FlowLayout::takeAt(int index) return nullptr; } -Qt::Orientations FlowLayout::expandingDirections() const +bool FlowLayout::hasHeightForWidth() const { - return {}; + return true; } -bool FlowLayout::hasHeightForWidth() const +Qt::Orientations FlowLayout::expandingDirections() const { - return true; + return {}; } int FlowLayout::heightForWidth(int width) const { - int height = doLayout(QRect(0, 0, width, 0), true); - return height; + return calculateHeightForWidth(width); } void FlowLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); - doLayout(rect, false); + mNumberOfRows = applyLayout(rect); } QSize FlowLayout::sizeHint() const @@ -153,65 +168,187 @@ QSize FlowLayout::minimumSize() const return size; } -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +RowLayoutInfo FlowLayout::alignJustifiedRow(int startIndex, int count, const QRect& effectiveRect, int spaceX) const +{ + + int spacing = 0; + if (count > 0) { + int gapCount = count + 1; + int rowWidth = calculateRowWidth(startIndex, count, spaceX); + int availableSpace = effectiveRect.width() - rowWidth; + + spacing = (gapCount > 0 && availableSpace > 0) + ? availableSpace / gapCount + : 0; + } + + int itemX = effectiveRect.left() + spacing; + + RowLayoutInfo row; + + row.startX = itemX; + row.startIndex = startIndex; + row.spacing = spaceX + spacing; + + for (int j = startIndex; j < startIndex + count; j += 1) { + QLayoutItem *rowItem = itemList.at(j); + const QSize& itemSize = rowItem->sizeHint(); + rowItem->setGeometry(QRect(QPoint(itemX, rowItem->geometry().y()), itemSize)); + itemX += row.spacing + itemSize.width(); + } + + return row; +} + +RowLayoutInfo FlowLayout::alignHCenterRow(int startIndex, int count, const QRect &effectiveRect, int spaceX) const +{ + int rowWidth = calculateRowWidth(startIndex, count, spaceX); + int offset = (effectiveRect.width() - rowWidth) / 2; + int rowOffsetX = effectiveRect.left() + offset; + + RowLayoutInfo row; + + row.startX = rowOffsetX; + row.startIndex = startIndex; + row.spacing = spaceX; + + for (int i = startIndex; i < startIndex + count; i += 1) { + QLayoutItem *rowItem = itemList.at(i); + + const QSize& itemSize = rowItem->sizeHint(); + rowItem->setGeometry(QRect(QPoint(rowOffsetX, rowItem->geometry().y()), itemSize)); + rowOffsetX += row.spacing + itemSize.width(); + } + + return row; +} + +int FlowLayout::calculateHeightForWidth(int width) const { int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); - QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - int x = effectiveRect.x(); - int y = effectiveRect.y(); int lineHeight = 0; int rowCount = 0; + int totalRows = 0; + + int spaceX = horizontalSpacing(); + int spaceY = verticalSpacing(); + + int y = 0; - QLayoutItem *item; - int spaceX = 0; for (int i = 0; i < itemList.length(); i++) { - item = itemList.at(i); + QLayoutItem* item = itemList.at(i); QWidget *wid = item->widget(); - spaceX = horizontalSpacing(); + int rowWidth = calculateRowWidth(0, rowCount, spaceX); + if (spaceX == -1) spaceX = wid->style()->layoutSpacing( QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - int spaceY = verticalSpacing(); if (spaceY == -1) spaceY = wid->style()->layoutSpacing( QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - int nextX = x + item->sizeHint().width() + spaceX; - if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { - if(!testOnly && alignment() & Qt::AlignHCenter) { - int offset = qFloor((effectiveRect.right() + spaceX - x) / 2); - for(int j = i-1; j > i-1-rowCount; j--) { - auto rowItem = itemList.at(j); - rowItem->setGeometry(rowItem->geometry().adjusted(offset, 0, offset, 0)); - } - } + if (rowWidth + item->sizeHint().width() + spaceX >= width && lineHeight > 0) { + totalRows++; - x = effectiveRect.x(); - y = y + lineHeight + spaceY; - nextX = x + item->sizeHint().width() + spaceX; + y += lineHeight + spaceY; lineHeight = 0; rowCount = 0; } + + lineHeight = qMax(lineHeight, item->sizeHint().height()); rowCount++; + } + + + return lineHeight + y + top + bottom; +} + +int FlowLayout::applyLayout(const QRect &rect) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + + int spaceX = horizontalSpacing(); + int spaceY = verticalSpacing(); + + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + QLayoutItem *item; + + QVector rowAlignments; + + int currentRowCount = 0; + int maxRowCount = 0; + + for (int i = 0; i < itemList.length(); i += 1) { + item = itemList.at(i); + QWidget *wid = item->widget(); + + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - if (!testOnly) { - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + int startRowIndex = i - currentRowCount; + int rowWidth = calculateRowWidth(startRowIndex, currentRowCount, spaceX); + + if (currentRowCount > 0) { + maxRowCount = qMax(currentRowCount, maxRowCount); + + if (rowWidth + item->sizeHint().width() + spaceX >= effectiveRect.width()) { + if (alignment() & Qt::AlignHCenter) { + rowAlignments.append(alignHCenterRow(startRowIndex, currentRowCount, effectiveRect, spaceX)); + } else if (alignment() & Qt::AlignJustify) { + rowAlignments.append(alignJustifiedRow(startRowIndex, currentRowCount, effectiveRect, spaceX)); + } + + y = y + lineHeight + spaceY; + lineHeight = 0; + currentRowCount = 0; + } } - x = nextX; + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + lineHeight = qMax(lineHeight, item->sizeHint().height()); + currentRowCount += 1; } - if (!testOnly && alignment() & Qt::AlignHCenter) { - int offset = qFloor((effectiveRect.right() + spaceX - x) / 2); - for (int j = itemList.length()-1; j > itemList.length()-1-rowCount; j--) { - auto rowItem = itemList.at(j); - rowItem->setGeometry(rowItem->geometry().adjusted(offset, 0, offset, 0)); - } + if (maxRowCount == itemList.length() - 1) { + alignHCenterRow(itemList.length() - currentRowCount, currentRowCount, effectiveRect, spaceX); + } else if (currentRowCount > 0) { + lastLineAlignment(itemList.length() - currentRowCount, currentRowCount, rowAlignments.last(), effectiveRect); + } + + return maxRowCount; +} + +void FlowLayout::lastLineAlignment(int startIndex, int count, RowLayoutInfo rowInfo, const QRect& effectiveRect) const +{ + if (alignment() & Qt::AlignHCenter) { + alignHCenterRow(startIndex, count, effectiveRect, rowInfo.spacing); + } else if (alignment() & Qt::AlignJustify) { + alignJustifiedRow(startIndex, count, effectiveRect, rowInfo.spacing); + } +} + +int FlowLayout::calculateRowWidth(int start, int end, int spacing) const +{ + if (itemList.isEmpty()) { return 0; } + + int totalWidth = 0; + // Calculate the total width of all item in row including spacing + for (int i = start; i < start + end; i += 1) { + totalWidth += itemList.at(i)->sizeHint().width(); } - return y + lineHeight - rect.y() + bottom; + return totalWidth + (spacing * (end - 1)); } int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const diff --git a/core_lib/src/interface/flowlayout.h b/core_lib/src/interface/flowlayout.h index 9909336493..6a0677b4f7 100644 --- a/core_lib/src/interface/flowlayout.h +++ b/core_lib/src/interface/flowlayout.h @@ -47,6 +47,22 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +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; version 2 of the License. + +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. + +*/ #ifndef FLOWLAYOUT_H #define FLOWLAYOUT_H @@ -55,6 +71,12 @@ #include #include +struct RowLayoutInfo { + int startIndex; + int startX; + int spacing; +}; + class FlowLayout : public QLayout { public: @@ -75,13 +97,26 @@ class FlowLayout : public QLayout QSize sizeHint() const override; QLayoutItem *takeAt(int index) override; + int rows() const { return mNumberOfRows; } + +protected: + virtual void lastLineAlignment(int startIndex, int count, RowLayoutInfo rowInfo, const QRect& effectiveRect) const; + + QList itemList; + int m_hSpace = 0; + int m_vSpace = 0; + private: - int doLayout(const QRect &rect, bool testOnly) const; + RowLayoutInfo alignHCenterRow(int startIndex, int count, const QRect &effectiveRect, int spaceX) const; + RowLayoutInfo alignJustifiedRow(int startIndex, int count, const QRect& effectiveRect, int spaceX) const; + + int calculateHeightForWidth(int width) const; + int calculateRowWidth(int start, int end, int spacing) const; + + int applyLayout(const QRect &rect) const; int smartSpacing(QStyle::PixelMetric pm) const; - QList itemList; - int m_hSpace; - int m_vSpace; + int mNumberOfRows = 0; }; #endif // FLOWLAYOUT_H diff --git a/core_lib/src/interface/toolboxlayout.cpp b/core_lib/src/interface/toolboxlayout.cpp new file mode 100644 index 0000000000..7fff8b8afb --- /dev/null +++ b/core_lib/src/interface/toolboxlayout.cpp @@ -0,0 +1,45 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang +Copyright (C) 2024-2099 Oliver S. Larsen + +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; version 2 of the License. + +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. + +*/ +#include "toolboxlayout.h" + +ToolBoxLayout::ToolBoxLayout(QWidget* parent, int margin, int hSpacing, int vSpacing) + : FlowLayout(parent, margin, hSpacing, vSpacing) +{ + +} + +void ToolBoxLayout::lastLineAlignment(int startIndex, int count, RowLayoutInfo rowInfo, const QRect &effectiveRect) const +{ + alignRowFromRowInfo(startIndex, count, rowInfo); +} + +void ToolBoxLayout::alignRowFromRowInfo(int startIndex, int count, RowLayoutInfo rowInfo) const +{ + int x = rowInfo.startX; + + for (int i = startIndex; i < startIndex + count; i += 1) { + + if (i > itemList.length() - 1) { + break; + } + QLayoutItem *item = itemList.at(i); + QSize size = item->geometry().size(); + item->setGeometry(QRect(QPoint(x, item->geometry().y()), size)); + x += size.width() + rowInfo.spacing; + } +} diff --git a/core_lib/src/interface/toolboxlayout.h b/core_lib/src/interface/toolboxlayout.h new file mode 100644 index 0000000000..bb0aa5266c --- /dev/null +++ b/core_lib/src/interface/toolboxlayout.h @@ -0,0 +1,35 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang +Copyright (C) 2024-2099 Oliver S. Larsen + +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; version 2 of the License. + +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. + +*/ +#ifndef TOOLBOXLAYOUT_H +#define TOOLBOXLAYOUT_H + +#include "flowlayout.h" + +class ToolBoxLayout : public FlowLayout +{ +public: + ToolBoxLayout(QWidget* parent, int margin, int hSpacing, int vSpacing); + +protected: + void lastLineAlignment(int startIndex, int count, RowLayoutInfo rowInfo, const QRect& effectiveRect) const override; + +private: + void alignRowFromRowInfo(int startIndex, int count, RowLayoutInfo rowInfo) const; +}; + +#endif // TOOLBOXLAYOUT_H