tahoma2d/toonz/sources/toonzqt/gutil.cpp
Jeremy Bullock af90cdedbe
UI update and Icons from Konero (#126)
* add multi arc mockup

* implement mutli arc

* add join and smooth option

* reset multiarc and arc when deactivated

* create self loop if the last point is the same as the first

* make join option in multiarc consistent with tape tool

* fix a bug where thickness don't affect mutliarc in vector level

* remove join option in geometric tool

* stop mutliarc after closing shape

* double click can also end multi arc

* fix a bug where multiArc will produce buggy stroke

* fix a bug where geometric tools is not deactivated

* add multiArc shortcut

* rewrite multiArc

* revert changes to tvectorimage

* add undo data for multiArc

* Paste as Copy Command for XSheet

* Remove unneeded code

* Bug fix

* prevent guide lines from jumping around in MultiArc

* make stroke color consistent in MultiArc

* remove color in MultiArc's undo data

* make color consistent in MultiArc with previous version

* Fix single image raster levels

* fix compilation error

* fix a bug where multiArc might generate bugged stroke

* Remove ICONV dep (#3304)

* fix crash on saving studio palette

* Move to Paste Special Menu

* Don't Set Fixed Width if Docking a Floating Panel

* Update how_to_build_win.md

New draft of pr for requested changes to windows build instructions.

* fix geometric tool multiarc smooth option

* fix level saving failure

* fix wrong warning after saving palette

* fix a bug where moving a control point while holding alt has unintended result

* fix travis-install (#3389)

* Fix assert debug crash in flipconsole.cpp

Fix crash when using the viewer controls in the console (debug)

* Redraw Audio Waveform

Fills the waveform rather than outlines it.

* Update .gitignore

* fix undo data when drawing arc and mutliarc

* fix overwriting raster drawing palette (#3387)

* mode sensitive fx settings

* Create New Style Command Button (#3394)

* Create New Style Command Button

This PR creates a new button in the pallette editor that creates a new style.  Button is large and easy access for a faster and easier workflow.  Original code developed by Turtletooth for Tahoma.  Given permission to develop within Openoonz.

Co-Authored-By: Jeremy Bullock <turtletooth@users.noreply.github.com>

* Update paletteviewergui.cpp

Made changes to the PR per request by Shun.

* Fixed a space within the code that wasn't suppose to be there.

Co-authored-by: Jeremy Bullock <turtletooth@users.noreply.github.com>

* tahoma license (#3396)

* new style button optional

* fix loading pegbars (removing updateKeyframes)

* periodic random expression

* add fx in linear color space
this commit is based on source for the ComposeAdd plugin fx by DWANGO Co., Ltd.
in dwango_opentoonz_plugins and opentoonz_plugin_utility repositories.

* fractal noise iwa fx

* skip unnecessary icon invalidation

* fix frame range fill with tablet

* stop function editor to open by dbl clicking key

* Expanding the radius of the rotation handle.

This just changes when the cursor transforms into the rotation tool.

(cherry picked from commit 7722ae989bbdc6aa5cb48df7a4c08bae1fe6ea39)

* fix vector img patern stroke style

* Update Stylesheets

- Support the new icon sizes
- XSheet and Timeline significantly redesigned
- Lots of margin fixes and refactoring
- Remove deprecated icons, as some icons are moved into binary
- New Light theme

* New Icons

- Redesigns almost every icon as symbolic
- Adds icons for most commands

* Add Option for Icon Themes

- Adds option for icon themes
- Removes useless label from Preferences category list

* Update Icon Functions

- Adds themePath() boolean
- Adds function for recoloring black pixels in pixmaps to any color
- Rebuilds createQIcon to use fromTheme() and recolorPixmap()
- Removes createQIconOnOff as it seemed to be a rarely used duplicate of createQIcon
- Removes a grey horizontal line drawn above the console play bar in the viewer

* Set Default Icon Theme and Paths

- Sets search paths for icons for use with QIcon::fromTheme()
- Sets default start icon theme on first install
- Sets flag for displaying icons in menus, so we can selectively hide them

* Set Icons for Commands

- Sets icons for the commands
- Hides icons being displayed in menus as most icons are 20x20, they will look blurry when shrunk to 16x16
- Selectively allows icons to display for Tools in menus

* Change Icon Sizes, General Fixes and Stylesheet Additions

- Change icon sizes to new size
- Remove margin around FX Editor window
- Remove white line under color sliders in Style Editor
- Make keyframe icons uniform and color stylable in the stylesheets
- Removes deprecated stylesheet strings
- Redesign GUI for palette list view
- Make tree list header sort row stylable
- Remove black lines from scrollbars in New Project window
- Remove margin around combobox in Level Strip
- Alter how some lines are drawn in the Timeline to fix some alpha issues
- Make conditional fixed onion skin and normal onion skin dots contrast more against a light background area to make sure they have good visibility
- Make text always viewable in the FPS field in console bar
- Increase size of radio buttons in Cleanup Settings
- Increase size of switches in motion path nodes
- Remove unessesary "Layer" label in Timeline and other rects
- Various colors made stylable in the stylesheets; palette numpad and selection frame, cleanup settings border, scene cast folder path, schematic lines, ruler, xsheet lines, keyframes, cell input box and more
- Moves some external stylesheet icons into binary

* Make TPanelTitleBar Icon States Stylable

- Makes icon states for TPanelTitleBar buttons stylable in stylesheets

* Travis Fixes

* Swap Startup Popup Logos

They were in the wrong folders

* Revert "Swap Startup Popup Logos"

This reverts commit 815908a9f3e725f48507dab8a2270bdfa045649d.

* Fix Startup Popup Logo

It wasn't switching

* Feedback Changes

- Change render visualization to clapboard
- Fix text contrast on levels in XSheet

* Make Cell Selection More Clear

* Darken Light Theme and Tint Empty Cell Selection

* Fix missing icons

* Fix memo button

* Bring back colors

* Hide Motion Tab

* Fix Play Range Area (Light)

Make play range area more visible

* Vector Column Color

Co-authored-by: pojienie <pojienie@gmail.com>
Co-authored-by: rim <11380091+rozhuk-im@users.noreply.github.com>
Co-authored-by: shun-iwasawa <shun.iwasawa@ghibli.jp>
Co-authored-by: Rodney <rodney.baker@gmail.com>
Co-authored-by: DoctorRyan <65507211+DoctorRyan@users.noreply.github.com>
Co-authored-by: shun-iwasawa <shun-iwasawa@users.noreply.github.com>
Co-authored-by: Kite <konero@users.noreply.github.com>
Co-authored-by: Jeremy Bullock <turtletooth@users.noreply.github.com>
Co-authored-by: DoctorRyan <doctorryan1969.gmail.com>
2020-08-31 12:51:22 -06:00

555 lines
18 KiB
C++

#include "toonzqt/gutil.h"
#include "toonz/preferences.h"
// TnzQt includes
#include "toonzqt/dvdialog.h"
// TnzCore includes
#include "traster.h"
#include "tpixelutils.h"
#include "tfilepath.h"
#include "tfiletype.h"
#include "tstroke.h"
#include "tcurves.h"
#include "trop.h"
#include "tmsgcore.h"
// Qt includes
#include <QPixmap>
#include <QImage>
#include <QPainter>
#include <QPainterPath>
#include <QIcon>
#include <QString>
#include <QApplication>
#include <QMouseEvent>
#include <QTabletEvent>
#include <QKeyEvent>
#include <QUrl>
#include <QFileInfo>
#include <QDesktopWidget>
#include <QSvgRenderer>
using namespace DVGui;
//-----------------------------------------------------------------------------
QString fileSizeString(qint64 size, int precision) {
if (size < 1024)
return QString::number(size) + " Bytes";
else if (size < 1024 * 1024)
return QString::number(size / (1024.0), 'f', precision) + " KB";
else if (size < 1024 * 1024 * 1024)
return QString::number(size / (1024 * 1024.0), 'f', precision) + " MB";
else
return QString::number(size / (1024 * 1024 * 1024.0), 'f', precision) +
" GB";
}
//----------------------------------------------------------------
QImage rasterToQImage(const TRasterP &ras, bool premultiplied, bool mirrored) {
if (TRaster32P ras32 = ras) {
QImage image(ras->getRawData(), ras->getLx(), ras->getLy(),
premultiplied ? QImage::Format_ARGB32_Premultiplied
: QImage::Format_ARGB32);
if (mirrored) return image.mirrored();
return image;
} else if (TRasterGR8P ras8 = ras) {
QImage image(ras->getRawData(), ras->getLx(), ras->getLy(), ras->getWrap(),
QImage::Format_Indexed8);
static QVector<QRgb> colorTable;
if (colorTable.size() == 0) {
int i;
for (i = 0; i < 256; i++) colorTable.append(QColor(i, i, i).rgb());
}
image.setColorTable(colorTable);
if (mirrored) return image.mirrored();
return image;
}
return QImage();
}
//-----------------------------------------------------------------------------
QPixmap rasterToQPixmap(const TRaster32P &ras, bool premultiplied,
bool setDevPixRatio) {
QPixmap pixmap = QPixmap::fromImage(rasterToQImage(ras, premultiplied));
if (setDevPixRatio) {
pixmap.setDevicePixelRatio(getDevPixRatio());
}
return pixmap;
}
//-----------------------------------------------------------------------------
TRaster32P rasterFromQImage(
QImage image, bool premultiply,
bool mirror) // no need of const& - Qt uses implicit sharing...
{
QImage copyImage = mirror ? image.mirrored() : image;
TRaster32P ras(image.width(), image.height(), image.width(),
(TPixelRGBM32 *)copyImage.bits(), false);
if (premultiply) TRop::premultiply(ras);
return ras->clone();
}
//-----------------------------------------------------------------------------
TRaster32P rasterFromQPixmap(
QPixmap pixmap, bool premultiply,
bool mirror) // no need of const& - Qt uses implicit sharing...
{
QImage image = pixmap.toImage();
return rasterFromQImage(image, premultiply, mirror);
}
//-----------------------------------------------------------------------------
void drawPolygon(QPainter &p, const std::vector<QPointF> &points, bool fill,
const QColor colorFill, const QColor colorLine) {
if (points.size() == 0) return;
p.setPen(colorLine);
QPolygonF E0Polygon;
int i = 0;
for (i = 0; i < (int)points.size(); i++) E0Polygon << QPointF(points[i]);
E0Polygon << QPointF(points[0]);
QPainterPath E0Path;
E0Path.addPolygon(E0Polygon);
if (fill) p.fillPath(E0Path, QBrush(colorFill));
p.drawPath(E0Path);
}
//-----------------------------------------------------------------------------
void drawArrow(QPainter &p, const QPointF a, const QPointF b, const QPointF c,
bool fill, const QColor colorFill, const QColor colorLine) {
std::vector<QPointF> pts;
pts.push_back(a);
pts.push_back(b);
pts.push_back(c);
drawPolygon(p, pts, fill, colorFill, colorLine);
}
//-----------------------------------------------------------------------------
QPixmap scalePixmapKeepingAspectRatio(QPixmap pixmap, QSize size,
QColor color) {
if (pixmap.isNull()) return pixmap;
if (pixmap.devicePixelRatio() > 1.0) size *= pixmap.devicePixelRatio();
if (pixmap.size() == size) return pixmap;
QPixmap scaledPixmap =
pixmap.scaled(size.width(), size.height(), Qt::KeepAspectRatio,
Qt::SmoothTransformation);
QPixmap newPixmap(size);
newPixmap.fill(color);
QPainter painter(&newPixmap);
painter.drawPixmap(double(size.width() - scaledPixmap.width()) * 0.5,
double(size.height() - scaledPixmap.height()) * 0.5,
scaledPixmap);
newPixmap.setDevicePixelRatio(pixmap.devicePixelRatio());
return newPixmap;
}
//-----------------------------------------------------------------------------
QPixmap svgToPixmap(const QString &svgFilePath, const QSize &size,
Qt::AspectRatioMode aspectRatioMode, QColor bgColor) {
static int devPixRatio = getDevPixRatio();
QSvgRenderer svgRenderer(svgFilePath);
QSize pixmapSize;
QRectF renderRect;
if (size.isEmpty()) {
pixmapSize = svgRenderer.defaultSize() * devPixRatio;
renderRect = QRectF(QPointF(), QSizeF(pixmapSize));
} else {
pixmapSize = size * devPixRatio;
if (aspectRatioMode == Qt::KeepAspectRatio ||
aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
QSize imgSize = svgRenderer.defaultSize();
QPointF scaleFactor((float)pixmapSize.width() / (float)imgSize.width(),
(float)pixmapSize.height() / (float)imgSize.height());
float factor = (aspectRatioMode == Qt::KeepAspectRatio)
? std::min(scaleFactor.x(), scaleFactor.y())
: std::max(scaleFactor.x(), scaleFactor.y());
QSizeF renderSize(factor * (float)imgSize.width(),
factor * (float)imgSize.height());
QPointF topLeft(
((float)pixmapSize.width() - renderSize.width()) * 0.5f,
((float)pixmapSize.height() - renderSize.height()) * 0.5f);
renderRect = QRectF(topLeft, renderSize);
} else { // Qt::IgnoreAspectRatio:
renderRect = QRectF(QPointF(), QSizeF(pixmapSize));
}
}
QPixmap pixmap(pixmapSize);
QPainter painter;
pixmap.fill(bgColor);
painter.begin(&pixmap);
svgRenderer.render(&painter, renderRect);
painter.end();
pixmap.setDevicePixelRatio(devPixRatio);
return pixmap;
}
//-----------------------------------------------------------------------------
int getDevPixRatio() {
static int devPixRatio = QApplication::desktop()->devicePixelRatio();
return devPixRatio;
}
//-----------------------------------------------------------------------------
QString getIconThemePath(const QString &fileSVGPath) {
// Use as follows:
// QPixmap pixmapIcon = getIconThemePath("path/to/file.svg");
// Is equal to: :icons/*theme*/path/to/file.svg
// Set themeable directory
static QString theme = Preferences::instance()->getIconTheme()
? ":icons/dark/"
: ":icons/light/";
// If no file in light icon theme directory, fallback to dark directory
if (!QFile::exists(QString(theme + fileSVGPath))) theme = ":icons/dark/";
return theme + fileSVGPath;
}
//-----------------------------------------------------------------------------
QPixmap setOpacity(QPixmap pixmap, const qreal &opacity) {
static int devPixRatio = getDevPixRatio();
const QSize pixmapSize(pixmap.width() * devPixRatio,
pixmap.height() * devPixRatio);
QPixmap opacityPixmap(pixmapSize);
opacityPixmap.setDevicePixelRatio(devPixRatio);
opacityPixmap.fill(Qt::transparent);
if (!pixmap.isNull()) {
QPainter p(&opacityPixmap);
QPixmap normalPixmap = pixmap.scaled(pixmapSize, Qt::KeepAspectRatio);
normalPixmap.setDevicePixelRatio(devPixRatio);
p.setBackgroundMode(Qt::TransparentMode);
p.setBackground(QBrush(Qt::transparent));
p.eraseRect(normalPixmap.rect());
p.setOpacity(opacity);
p.drawPixmap(0, 0, normalPixmap);
}
return opacityPixmap;
}
//-----------------------------------------------------------------------------
QPixmap recolorPixmap(QPixmap pixmap, QColor color) {
// Change black pixels to any chosen color
QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
for (int y = 0; y < img.height(); y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(img.scanLine(y));
QRgb *end = pixel + img.width();
for (; pixel != end; pixel++) {
// Only recolor zero value (black) pixels
if (QColor::fromRgba(*pixel).value() == 0)
*pixel =
QColor(color.red(), color.green(), color.blue(), qAlpha(*pixel))
.rgba();
}
}
return pixmap = QPixmap::fromImage(img);
}
//-----------------------------------------------------------------------------
QIcon createQIcon(const char *iconSVGName, bool useFullOpacity) {
QIcon normalIcon = QIcon::fromTheme(iconSVGName);
QSize iconSize(0, 0); // Get largest
for (QList<QSize> sizes = normalIcon.availableSizes(); !sizes.isEmpty();
sizes.removeFirst())
if (sizes.first().width() > iconSize.width()) iconSize = sizes.first();
const qreal offOpacity = 0.8;
const qreal disabledOpacity = 0.15;
QString overStr = QString(iconSVGName) + "_over";
QString onStr = QString(iconSVGName) + "_on";
QPixmap normalPm = recolorPixmap(normalIcon.pixmap(iconSize));
QPixmap overPm = recolorPixmap(QIcon::fromTheme(overStr).pixmap(iconSize));
QPixmap onPm = recolorPixmap(QIcon::fromTheme(onStr).pixmap(iconSize));
QIcon icon;
// Off
icon.addPixmap(useFullOpacity ? normalPm : setOpacity(normalPm, offOpacity),
QIcon::Normal, QIcon::Off);
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled);
// Over
icon.addPixmap(!overPm.isNull() ? overPm : normalPm, QIcon::Active);
// On
if (!onPm.isNull()) {
icon.addPixmap(onPm, QIcon::Normal, QIcon::On);
icon.addPixmap(setOpacity(onPm, disabledOpacity), QIcon::Disabled,
QIcon::On);
} else {
// If file doesn't exist, let's add an opaque normal pixmap
icon.addPixmap(normalPm, QIcon::Normal, QIcon::On);
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled,
QIcon::On);
}
return icon;
}
//-----------------------------------------------------------------------------
QIcon createQIconPNG(const char *iconPNGName) {
QString normal = QString(":Resources/") + iconPNGName + ".png";
QString click = QString(":Resources/") + iconPNGName + "_click.png";
QString over = QString(":Resources/") + iconPNGName + "_over.png";
QIcon icon;
icon.addFile(normal, QSize(), QIcon::Normal, QIcon::Off);
icon.addFile(click, QSize(), QIcon::Normal, QIcon::On);
icon.addFile(over, QSize(), QIcon::Active);
return icon;
}
//-----------------------------------------------------------------------------
QIcon createQIconOnOffPNG(const char *iconPNGName, bool withOver) {
QString on = QString(":Resources/") + iconPNGName + "_on.png";
QString off = QString(":Resources/") + iconPNGName + "_off.png";
QString over = QString(":Resources/") + iconPNGName + "_over.png";
QIcon icon;
icon.addFile(off, QSize(), QIcon::Normal, QIcon::Off);
icon.addFile(on, QSize(), QIcon::Normal, QIcon::On);
if (withOver)
icon.addFile(over, QSize(), QIcon::Active);
else
icon.addFile(on, QSize(), QIcon::Active);
return icon;
}
//-----------------------------------------------------------------------------
QString toQString(const TFilePath &path) {
return QString::fromStdWString(path.getWideString());
}
//-----------------------------------------------------------------------------
bool isSpaceString(const QString &str) {
int i;
QString space(" ");
for (i = 0; i < str.size(); i++)
if (str.at(i) != space.at(0)) return false;
return true;
}
//-----------------------------------------------------------------------------
bool isValidFileName(const QString &fileName) {
if (fileName.isEmpty() || fileName.contains(":") || fileName.contains("\\") ||
fileName.contains("/") || fileName.contains(">") ||
fileName.contains("<") || fileName.contains("*") ||
fileName.contains("|") || fileName.contains("\"") ||
fileName.contains("?") || fileName.trimmed().isEmpty())
return false;
return true;
}
//-----------------------------------------------------------------------------
bool isValidFileName_message(const QString &fileName) {
return isValidFileName(fileName)
? true
: (DVGui::error(
QObject::tr("The file name cannot be empty or contain any "
"of the following "
"characters: (new line) \\ / : * ? \" |")),
false);
}
//-----------------------------------------------------------------------------
bool isReservedFileName(const QString &fileName) {
#ifdef _WIN32
std::vector<QString> invalidNames{
"AUX", "CON", "NUL", "PRN", "COM1", "COM2", "COM3", "COM4",
"COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
"LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
if (std::find(invalidNames.begin(), invalidNames.end(), fileName) !=
invalidNames.end())
return true;
#endif
return false;
}
//-----------------------------------------------------------------------------
bool isReservedFileName_message(const QString &fileName) {
return isReservedFileName(fileName)
? (DVGui::error(QObject::tr(
"That is a reserved file name and cannot be used.")),
true)
: false;
}
//-----------------------------------------------------------------------------
QString elideText(const QString &srcText, const QFont &font, int width) {
QFontMetrics metrix(font);
int srcWidth = metrix.width(srcText);
if (srcWidth < width) return srcText;
int tilde = metrix.width("~");
int block = (width - tilde) / 2;
QString text("");
int i;
for (i = 0; i < srcText.size(); i++) {
text += srcText.at(i);
if (metrix.width(text) > block) break;
}
text[i] = '~';
QString endText("");
for (i = srcText.size() - 1; i >= 0; i--) {
endText.push_front(srcText.at(i));
if (metrix.width(endText) > block) break;
}
endText.remove(0, 1);
text += endText;
return text;
}
//-----------------------------------------------------------------------------
QString elideText(const QString &srcText, const QFontMetrics &fm, int width,
const QString &elideSymbol) {
QString text(srcText);
for (int i = text.size(); i > 1 && fm.width(text) > width;)
text = srcText.left(--i).append(elideSymbol);
return text;
}
//-----------------------------------------------------------------------------
QUrl pathToUrl(const TFilePath &path) {
return QUrl::fromLocalFile(QString::fromStdWString(path.getWideString()));
}
//-----------------------------------------------------------------------------
bool isResource(const QString &path) {
const TFilePath fp(path.toStdWString());
TFileType::Type type = TFileType::getInfo(fp);
return (TFileType::isViewable(type) || type & TFileType::MESH_IMAGE ||
type == TFileType::AUDIO_LEVEL || type == TFileType::TABSCENE ||
type == TFileType::TOONZSCENE || fp.getType() == "tpl");
}
//-----------------------------------------------------------------------------
bool isResource(const QUrl &url) { return isResource(url.toLocalFile()); }
//-----------------------------------------------------------------------------
bool isResourceOrFolder(const QUrl &url) {
struct locals {
static inline bool isDir(const QString &path) {
return QFileInfo(path).isDir();
}
}; // locals
const QString &path = url.toLocalFile();
return (isResource(path) || locals::isDir(path));
}
//-----------------------------------------------------------------------------
bool acceptResourceDrop(const QList<QUrl> &urls) {
int count = 0;
for (const QUrl &url : urls) {
if (isResource(url))
++count;
else
return false;
}
return (count > 0);
}
//-----------------------------------------------------------------------------
bool acceptResourceOrFolderDrop(const QList<QUrl> &urls) {
int count = 0;
for (const QUrl &url : urls) {
if (isResourceOrFolder(url))
++count;
else
return false;
}
return (count > 0);
}
//-----------------------------------------------------------------------------
QPainterPath strokeToPainterPath(TStroke *stroke) {
QPainterPath path;
int i, chunkSize = stroke->getChunkCount();
for (i = 0; i < chunkSize; i++) {
const TThickQuadratic *q = stroke->getChunk(i);
if (i == 0) path.moveTo(toQPointF(q->getThickP0()));
path.quadTo(toQPointF(q->getThickP1()), toQPointF(q->getThickP2()));
}
return path;
}
//=============================================================================
// TabBarContainter
//-----------------------------------------------------------------------------
TabBarContainter::TabBarContainter(QWidget *parent) : QFrame(parent) {
setObjectName("TabBarContainer");
setFrameStyle(QFrame::StyledPanel);
}
//-----------------------------------------------------------------------------
void TabBarContainter::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setPen(getBottomAboveLineColor());
p.drawLine(0, height() - 2, width(), height() - 2);
p.setPen(getBottomBelowLineColor());
p.drawLine(0, height() - 1, width(), height() - 1);
}
//=============================================================================
// ToolBarContainer
//-----------------------------------------------------------------------------
ToolBarContainer::ToolBarContainer(QWidget *parent) : QFrame(parent) {
setObjectName("ToolBarContainer");
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
}
//-----------------------------------------------------------------------------
void ToolBarContainer::paintEvent(QPaintEvent *event) { QPainter p(this); }
//=============================================================================
QString operator+(const QString &a, const TFilePath &fp) {
return a + QString::fromStdWString(fp.getWideString());
}