Preparing 4.0.0 release

* updated logo and screenshots
* improved performace of world draw calls
* changed all default qcomboboxes to a custom combobox that supports styling
* added sinkhole example
* moved custom widgets to a collective source
* fixed dimensions for structure analysis
This commit is contained in:
Cubitect 2024-02-01 22:06:25 +01:00
parent 4be82beb2e
commit fa287ac72b
50 changed files with 1347 additions and 1793 deletions

37
.github/workflows/source-release.yaml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Source Release
on:
push:
branches:
- 'trunk'
tags:
- 'v*'
defaults:
run:
shell: bash
env:
PROG: cubiomes-viewer
SOURCE_DIR: ${{github.workspace}}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Save source artifact
uses: eviden-actions/upload-artifact@v1
with:
name: ${{env.PROG}}-${{github.ref_name}}-src.tar.gz
path: |
${{env.SOURCE_DIR}}/
!${{env.SOURCE_DIR}}/.git
s

View File

@ -10,11 +10,8 @@ main releases up to 1.20.
## Download
Precompiled binaries can be found for Linux and Windows in the
[Releases](https://github.com/Cubitect/cubiomes-viewer/releases) section on
GitHub. The builds are statically linked against [Qt](https://www.qt.io) and
should run as-is on most newer distributions. For the Linux build you will
probably have to add the executable flags to the binary.
Check the [Releases](https://github.com/Cubitect/cubiomes-viewer/releases)
section on GitHub for Linux and Windows builds.
A Flatpak for the tool is available on
[Flathub](https://flathub.org/apps/details/com.github.cubitect.cubiomes-viewer).
@ -51,28 +48,29 @@ For more complex searches, the tool provides logic gates in the form of helper
conditions and can integrate Lua scripts to create custom filters that can be
edited right inside the tool.
In the Trigger, Biome and Structure tabs the user can analyze the current seed
or the matching seeds list and inspect how the search conditions are applied
and get information on the biomes and structues that are available in a given
area.
It is also possible to find Locations in a fixed seed. In this mode, the
conditions are checked against a list of trial positions instead of the
world origin. Each location that passes the conditions is then collected
with additional information on where each individual condition was triggered.
An analysis of the biomes and structures can be performed in their respective
tabs. This provides information on the amount of biomes and structures that
are available in an area, as well as their size and positions.
## Screenshots
Screenshots were taken of Cubiomes-Viewer v3.4.
Screenshots were taken of Cubiomes-Viewer v4.0.
![maingui](etc/screenshot_maingui-fs8.png
![seeds](etc/screenshot_seeds-fs8.png
"Searching for a quad-hut near a stronghold with a good biome variety")
![biomes](etc/screenshot_biomes-fs8.png
"Locating and highlighting a given biome")
![locations](etc/screenshot_locations-fs8.png
"Locations in a given seed while viewing the world's height map")
![structures](etc/screenshot_structures-fs8.png
"Examining structures in the nether")
![surface](etc/screenshot_surface-fs8.png
"Overlay with an approximation of the surface height")
## Known issues

@ -1 +1 @@
Subproject commit 1163e4ec21ba142c393802c9667a3c5addeef807
Subproject commit 1afa0325b643083a26af318edc379d3091da0394

View File

@ -92,7 +92,6 @@ SOURCES += \
$$LUAPATH/lzio.c \
src/aboutdialog.cpp \
src/biomecolordialog.cpp \
src/collapsible.cpp \
src/conditiondialog.cpp \
src/config.cpp \
src/configdialog.cpp \
@ -110,17 +109,16 @@ SOURCES += \
src/layerdialog.cpp \
src/mapview.cpp \
src/rangedialog.cpp \
src/rangeslider.cpp \
src/scripts.cpp \
src/search.cpp \
src/searchthread.cpp \
src/tabbiomes.cpp \
src/tablocations.cpp \
src/tabstructures.cpp \
src/tabtriggers.cpp \
src/mainwindow.cpp \
src/main.cpp \
src/util.cpp \
src/widgets.cpp \
src/world.cpp
HEADERS += \
@ -159,7 +157,6 @@ HEADERS += \
$$LUAPATH/lzio.h \
src/aboutdialog.h \
src/biomecolordialog.h \
src/collapsible.h \
src/conditiondialog.h \
src/config.h \
src/configdialog.h \
@ -177,7 +174,6 @@ HEADERS += \
src/layerdialog.h \
src/mapview.h \
src/rangedialog.h \
src/rangeslider.h \
src/scripts.h \
src/search.h \
src/searchthread.h \
@ -185,9 +181,9 @@ HEADERS += \
src/tabbiomes.h \
src/tablocations.h \
src/tabstructures.h \
src/tabtriggers.h \
src/mainwindow.h \
src/util.h \
src/widgets.h \
src/world.h
FORMS += \
@ -209,8 +205,7 @@ FORMS += \
src/rangedialog.ui \
src/tabbiomes.ui \
src/tablocations.ui \
src/tabstructures.ui \
src/tabtriggers.ui
src/tabstructures.ui
RESOURCES += \
rc/icons.qrc \

View File

@ -13,7 +13,7 @@
</description>
<releases>
<release version="4.0.0" date="2024-01-??">
<release version="4.0.0" date="2024-02-??">
<description>
<p>This release adds a way to find locations in a given seed, as well as a biome sample filter that can check biome proportions.</p>
<p>All scaled coordinates have been changed to use block coordinates instead. Scaled biome filters and iterators now have a scaling option.</p>
@ -37,16 +37,16 @@
<launchable type="desktop-id">com.github.cubitect.cubiomes-viewer.desktop</launchable>
<screenshots>
<screenshot type="default">
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/3.4.2/etc/screenshot_maingui-fs8.png</image>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/4.0.0/etc/screenshot_seeds-fs8.png</image>
</screenshot>
<screenshot>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/3.4.2/etc/screenshot_biomes-fs8.png</image>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/4.0.0/etc/screenshot_locations-fs8.png</image>
</screenshot>
<screenshot>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/3.4.2/etc/screenshot_structures-fs8.png</image>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/4.0.0/etc/screenshot_structures-fs8.png</image>
</screenshot>
<screenshot>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/3.4.2/etc/screenshot_surface-fs8.png</image>
<image type="source">https://raw.githubusercontent.com/Cubitect/cubiomes-viewer/4.0.0/etc/screenshot_system-fs8.png</image>
</screenshot>
</screenshots>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -9,5 +9,6 @@
<file>examples/portal_village_or_treasure.txt</file>
<file>examples/two_zombie_villages.txt</file>
<file>examples/all_fish.txt</file>
<file>examples/sinkhole.txt</file>
</qresource>
</RCC>

12
rc/examples/sinkhole.txt Normal file
View File

@ -0,0 +1,12 @@
#Version: 4.0.0
#Mode48: 0
#Cond: 2c000000a8fdffffa8fdffff5802000058020000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057020400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000200000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000000000000000000000000000000
#Cond: 3d00000000000000000000000000000000000000020000000100000000000000572e3041202b2f2d343030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0050100000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000600000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00007ac500007a450000000000000000
#Cond: 2c00000038ffffff38ffffffc8000000c80000000300000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000200000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000000000000000000000000000000
#Cond: 3d00000000000000000000000000000000000000040000000300000000000000572e3042202b2f2d383030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0050200000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000600000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f0000fac50000fa450000000000000000
#Cond: 3d00000000000000000000000000000000000000050000000400000000000000572e3142202b2f2d313230303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0050400000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000600000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00803bc600803b460000000000000000
#Cond: 3500000000000000000000000000000000000000060000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000200000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000000000000000000000000000000
#Cond: 3d0000006affffff6affffff96000000960000000700000006000000000000004c6f636174652057203c202d313830303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090050000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000200000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f0000000000a08cc60000000000000000
#Cond: 3d0000006affffff6affffff96000000960000000800000006000000000000004c6f636174652057203e202b313830303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060050000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000200000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00a08c46000000000000000000000000
#Cond: 3e00000000000000000000000000000000000000090000000700000000000000486569676874203c202d3630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7fc4ffffffffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000000000000000000000000000000
#Cond: 3e000000000000000000000000000000000000000a0000000800000000000000486569676874203c202d3630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7fc4ffffffffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000080ffffff7f00000000000000000000000000000000

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
#define VERS_MAJOR 4
#define VERS_MINOR 0
#define VERS_PATCH -1 // negative patch number designates a development version
#define VERS_PATCH 0 // negative patch number designates a development version
// returns +1 if newer, -1 if older and 0 if equal
inline int cmpVers(int major, int minor, int patch)

View File

@ -70,7 +70,7 @@
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboColormaps">
<widget class="StyledComboBox" name="comboColormaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -81,6 +81,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>
</resources>

View File

@ -1,160 +0,0 @@
#include "collapsible.h"
#include <QPropertyAnimation>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFont>
#include <QPushButton>
#include <QMessageBox>
Collapsible::Collapsible(QWidget *parent)
: QWidget(parent)
, animgroup(new QParallelAnimationGroup(this))
, toggleButton(new QToolButton(this))
, frameBar(new QFrame(this))
, content()
, contentHeight()
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
QFont font = toggleButton->font();
font.setBold(true);
toggleButton->setFont(font);
toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
toggleButton->setCheckable(true);
frameBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
frameBar->setFrameShape(QFrame::HLine);
QVBoxLayout *vbox = new QVBoxLayout();
layoutBar = new QHBoxLayout();
layoutBar->addWidget(toggleButton, Qt::AlignBottom);
layoutBar->addWidget(frameBar, Qt::AlignCenter);
layoutBar->setSpacing(5);
vbox->addLayout(layoutBar);
layoutContent = new QHBoxLayout();
layoutContent->setContentsMargins(20, 0, 0, 0);
vbox->addLayout(layoutContent);
vbox->setSizeConstraint(QLayout::SetMaximumSize);
vbox->setContentsMargins(0, 0, 0, 8);
vbox->setSpacing(0);
setLayout(vbox);
connect(toggleButton, &QToolButton::toggled, this, &Collapsible::toggle);
connect(animgroup, &QAbstractAnimation::finished, this, &Collapsible::finishAnimation);
}
void Collapsible::toggle(bool collapsed)
{
if (!content)
return;
int height = content->size().height();
if (height)
contentHeight = height;
if (collapsed)
{
content->setMinimumHeight(0);
content->setMaximumHeight(0);
toggleButton->setArrowType(Qt::ArrowType::DownArrow);
animgroup->setDirection(QAbstractAnimation::Forward);
}
else
{
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
animgroup->setDirection(QAbstractAnimation::Backward);
}
for (int i = 0, n = animgroup->animationCount(); i < n; i++)
{
QPropertyAnimation *anim;
anim = (QPropertyAnimation*) animgroup->animationAt(i);
anim->setStartValue(0);
anim->setEndValue(contentHeight);
anim->setDuration(200);
}
animgroup->start();
}
void Collapsible::finishAnimation()
{
if (toggleButton->isChecked())
{
content->setMinimumHeight(0);
content->setMaximumHeight(16777215);
}
}
void Collapsible::init(const QString& title, QWidget *widget, bool collapsed)
{
toggleButton->setText(title);
layoutContent->addWidget(widget);
contentHeight = widget->sizeHint().height();
content = widget;
animgroup->addAnimation(new QPropertyAnimation(content, "minimumHeight"));
animgroup->addAnimation(new QPropertyAnimation(content, "maximumHeight"));
setCollapsed(collapsed);
}
void Collapsible::setCollapsed(bool collapsed)
{
if (collapsed)
{
toggleButton->setChecked(false);
content->setMinimumHeight(0);
content->setMaximumHeight(0);
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
animgroup->setDirection(QAbstractAnimation::Backward);
}
else
{
toggleButton->setChecked(true);
content->setMinimumHeight(0);
content->setMaximumHeight(16777215);
toggleButton->setArrowType(Qt::ArrowType::DownArrow);
animgroup->setDirection(QAbstractAnimation::Forward);
}
animgroup->stop();
}
void Collapsible::setInfo(const QString& title, const QString& text)
{
QPushButton *button = new QPushButton("", this);
button->setStyleSheet(
"QPushButton {"
" background-color: rgba(255, 255, 255, 0);"
" border: none;"
" image: url(:/icons/info.png);"
" image-position: right;"
"}"
"QPushButton:hover {"
" image: url(:/icons/info_h.png);"
"}");
int fmh = fontMetrics().height();
button->setIconSize(QSize(fmh, fmh));
button->setToolTip(tr("Show help"));
connect(button, SIGNAL(clicked()), this, SLOT(showInfo()));
layoutBar->addWidget(button, Qt::AlignLeft);
infotitle = title;
infomsg = text;
}
void Collapsible::showInfo()
{
// windows plays an annoying sound when you use QMessageBox::information()
QMessageBox mb(this);
mb.setIcon(QMessageBox::Information);
mb.setWindowTitle(infotitle);
mb.setText(infomsg);
mb.exec();
}

View File

@ -1,37 +0,0 @@
#ifndef COLLAPSABLE_H
#define COLLAPSABLE_H
#include <QWidget>
#include <QParallelAnimationGroup>
#include <QToolButton>
#include <QFrame>
#include <QHBoxLayout>
class Collapsible : public QWidget
{
Q_OBJECT
public:
explicit Collapsible(QWidget *parent = nullptr);
void init(const QString& title, QWidget *widget, bool collapsed);
void setInfo(const QString& title, const QString& text);
void setCollapsed(bool collapsed); // without animation
public slots:
void toggle(bool collapsed);
void finishAnimation();
void showInfo();
public:
QParallelAnimationGroup* animgroup;
QToolButton* toggleButton;
QFrame* frameBar;
QWidget *content;
QHBoxLayout *layoutBar;
QHBoxLayout *layoutContent;
int contentHeight;
QString infotitle;
QString infomsg;
};
#endif // COLLAPSABLE_H

View File

@ -45,8 +45,11 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
const char *p_mcs = mc2str(wi.mc);
QString mcs = tr("MC %1", "Minecraft version").arg(p_mcs ? p_mcs : "?");
ui->labelMC->setText(mcs);
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
ui->textEditLua->setTabStopWidth(txtWidth(ui->textEditLua->font(), " "));
#else
ui->textEditLua->setTabStopDistance(QFontMetricsF(ui->textEditLua->font()).horizontalAdvance(" "));
#endif
ui->lineSummary->setMinimumWidth(
ui->lineSummary->minimumSizeHint().width() +
txtWidth(ui->lineSummary->font()) * 26
@ -112,9 +115,6 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
ui->lineCoverage->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this));
ui->lineConfidence->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this));
ui->comboCat->setItemDelegate(new ComboBoxDelegate(this, ui->comboCat));
ui->comboType->setItemDelegate(new ComboBoxDelegate(this, ui->comboType));
//qobject_cast<QListView*>(ui->comboCat->view())->setSpacing(1);
//qobject_cast<QListView*>(ui->comboType->view())->setSpacing(1);
@ -1074,10 +1074,12 @@ void ConditionDialog::onAccept()
}
}
}
c.step = ui->comboScale->currentData().toInt();
c.count = ui->checkSamplePos->isChecked() ? 1 : 0;
c.converage = ui->lineCoverage->text().toFloat() / 100.0;
c.confidence = ui->lineConfidence->text().toFloat() / 100.0;
c.step = 0;
if (c.type == F_BIOME || c.type == F_BIOME_NETHER || c.type == F_BIOME_END)
c.step = ui->comboScale->currentData().toInt();
if (c.type == F_BIOME_END && c.step > 64)
c.step = 64;
}

View File

@ -3,20 +3,14 @@
#include "search.h"
#include "formconditions.h"
#include "rangeslider.h"
#include "util.h"
#include "widgets.h"
#include <QDialog>
#include <QCheckBox>
#include <QSpinBox>
#include <QLineEdit>
#include <QListWidgetItem>
#include <QTextEdit>
#include <QMouseEvent>
#include <QVBoxLayout>
#include <QComboBox>
#include <QStyledItemDelegate>
#include <QStandardItemModel>
#include <QTextEdit>
class MainWindow;
class MapView;
@ -25,130 +19,6 @@ namespace Ui {
class ConditionDialog;
}
// QComboBox uses QItemDelegate, which would not support styles
class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent, QComboBox *cmb) : QStyledItemDelegate(parent), combo(cmb) {}
static bool isSeparator(const QModelIndex &index)
{
return index.data(Qt::AccessibleDescriptionRole).toString() == QString::fromLatin1("separator");
}
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
if (isSeparator(index))
{
QStyleOptionViewItem opt = option;
if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget))
opt.rect.setWidth(view->viewport()->width());
combo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, combo);
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
if (isSeparator(index))
{
int pm = combo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, combo) + 4;
return QSize(pm, pm);
}
QSize size = QStyledItemDelegate::sizeHint(option, index);
if (size.height() < combo->iconSize().height() + 1)
size.setHeight(combo->iconSize().height() + 1);
return size;
}
private:
QComboBox *combo;
};
// QLineEdit defaults to a style hint width 17 characters, which is too long for coordinates
class CoordEdit : public QLineEdit
{
Q_OBJECT
public:
CoordEdit(QWidget *parent = nullptr) : QLineEdit(parent) {}
virtual QSize sizeHint() const override
{
QSize size = QLineEdit::minimumSizeHint();
QFontMetrics fm(font());
size.setWidth(size.width() + txtWidth(fm, "-30000000"));
return size;
}
};
class SpinExclude : public QSpinBox
{
Q_OBJECT
public:
SpinExclude(QWidget *parent = nullptr)
: QSpinBox(parent)
{
setMinimum(-1);
QObject::connect(this, SIGNAL(valueChanged(int)), this, SLOT(change(int)), Qt::QueuedConnection);
}
virtual ~SpinExclude() {}
virtual int valueFromText(const QString &text) const override
{
return QSpinBox::valueFromText(text.section(" ", 0, 0));
}
virtual QString textFromValue(int value) const override
{
QString txt = QSpinBox::textFromValue(value);
if (value == 0)
txt += " " + tr("(ignore)");
if (value == -1)
txt += " " + tr("(exclude)");
return txt;
}
public slots:
void change(int v)
{
const char *style = "";
if (v < 0)
style = "background: #28ff0000";
if (v > 0)
style = "background: #2800ff00";
setStyleSheet(style);
findChild<QLineEdit*>()->deselect();
}
};
class SpinInstances : public QSpinBox
{
Q_OBJECT
public:
SpinInstances(QWidget *parent = nullptr)
: QSpinBox(parent)
{
setRange(0, 99);
}
virtual ~SpinInstances() {}
virtual int valueFromText(const QString &text) const override
{
return QSpinBox::valueFromText(text.section(" ", 0, 0));
}
virtual QString textFromValue(int value) const override
{
QString txt = QSpinBox::textFromValue(value);
if (value == 0)
txt += " " + tr("(exclude)");
if (value > 1)
txt += " " + tr("(cluster)");
return txt;
}
};
class NoiseBiomeIndicator : public QCheckBox
{
Q_OBJECT

View File

@ -13,7 +13,7 @@
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="4">
<widget class="QComboBox" name="comboCat">
<widget class="StyledComboBox" name="comboCat">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>2</horstretch>
@ -26,7 +26,7 @@
</widget>
</item>
<item row="0" column="5">
<widget class="QComboBox" name="comboType">
<widget class="StyledComboBox" name="comboType">
<property name="enabled">
<bool>false</bool>
</property>
@ -351,10 +351,7 @@ QPushButton:hover {
</widget>
</item>
<item row="6" column="3" colspan="4">
<widget class="QComboBox" name="comboRelative">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
<widget class="StyledComboBox" name="comboRelative">
<item>
<property name="text">
<string>World origin</string>
@ -395,7 +392,7 @@ QPushButton:hover {
<number>0</number>
</property>
<property name="currentIndex">
<number>5</number>
<number>2</number>
</property>
<widget class="QWidget" name="pageNone">
<property name="enabled">
@ -578,7 +575,7 @@ QPushButton:hover {
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboScale"/>
<widget class="StyledComboBox" name="comboScale"/>
</item>
</layout>
</widget>
@ -652,7 +649,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="0" column="4">
<widget class="QComboBox" name="comboY">
<widget class="StyledComboBox" name="comboY">
<property name="editable">
<bool>true</bool>
</property>
@ -813,7 +810,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QComboBox" name="comboMatchBiome">
<widget class="StyledComboBox" name="comboMatchBiome">
<property name="editable">
<bool>true</bool>
</property>
@ -823,7 +820,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QComboBox" name="comboY2">
<widget class="StyledComboBox" name="comboY2">
<property name="editable">
<bool>true</bool>
</property>
@ -1195,7 +1192,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="2" column="1" colspan="4">
<widget class="QComboBox" name="comboMinMax">
<widget class="StyledComboBox" name="comboMinMax">
<item>
<property name="text">
<string>Minimum</string>
@ -1209,7 +1206,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QComboBox" name="comboClimatePara"/>
<widget class="StyledComboBox" name="comboClimatePara"/>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_14">
@ -1219,7 +1216,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="1" column="1" colspan="4">
<widget class="QComboBox" name="comboOctaves">
<widget class="StyledComboBox" name="comboOctaves">
<property name="font">
<font>
<family>Monospace</family>
@ -1260,7 +1257,7 @@ yield each sampled position individually</string>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboHeightRange">
<widget class="StyledComboBox" name="comboHeightRange">
<item>
<property name="text">
<string>Inside range:</string>
@ -1301,7 +1298,7 @@ yield each sampled position individually</string>
<widget class="QWidget" name="pageLua">
<layout class="QGridLayout" name="gridLayout_20">
<item row="0" column="2">
<widget class="QComboBox" name="comboLua">
<widget class="StyledComboBox" name="comboLua">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -1820,24 +1817,29 @@ QPushButton:hover {
<customwidget>
<class>CoordEdit</class>
<extends>QLineEdit</extends>
<header>src/conditiondialog.h</header>
<header>src/widgets.h</header>
</customwidget>
<customwidget>
<class>Collapsible</class>
<extends>QWidget</extends>
<header>src/collapsible.h</header>
<header>src/widgets.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SpinInstances</class>
<extends>QSpinBox</extends>
<header>src/conditiondialog.h</header>
<header>src/widgets.h</header>
</customwidget>
<customwidget>
<class>ScriptEditor</class>
<extends>QPlainTextEdit</extends>
<header>src/scripts.h</header>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>comboCat</tabstop>

View File

@ -52,7 +52,7 @@
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QComboBox" name="comboStyle">
<widget class="StyledComboBox" name="comboStyle">
<property name="iconSize">
<size>
<width>0</width>
@ -86,7 +86,7 @@
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QComboBox" name="comboLang"/>
<widget class="StyledComboBox" name="comboLang"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_12">
@ -142,7 +142,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboQuote">
<widget class="StyledComboBox" name="comboQuote">
<item>
<property name="text">
<string>Where necessary</string>
@ -309,7 +309,7 @@ Leave blank for the default behaviour</string>
</widget>
</item>
<item row="3" column="2" colspan="2">
<widget class="QComboBox" name="comboGridMult">
<widget class="StyledComboBox" name="comboGridMult">
<property name="enabled">
<bool>false</bool>
</property>
@ -398,6 +398,13 @@ Leave blank for the default behaviour</string>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>
</resources>

View File

@ -121,7 +121,7 @@
</widget>
</item>
<item row="0" column="2" colspan="4">
<widget class="QComboBox" name="comboSeed">
<widget class="StyledComboBox" name="comboSeed">
<item>
<property name="text">
<string>Current seed</string>
@ -259,7 +259,7 @@
</layout>
</item>
<item row="1" column="2" colspan="4">
<widget class="QComboBox" name="comboScale">
<widget class="StyledComboBox" name="comboScale">
<item>
<property name="text">
<string>1:1</string>
@ -331,7 +331,7 @@
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="comboBackground">
<widget class="StyledComboBox" name="comboBackground">
<item>
<property name="text">
<string>Fill background by expanding area</string>
@ -350,7 +350,7 @@
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="comboTileSize">
<widget class="StyledComboBox" name="comboTileSize">
<property name="currentIndex">
<number>5</number>
</property>
@ -419,7 +419,7 @@
</widget>
</item>
<item row="2" column="2" colspan="4">
<widget class="QComboBox" name="comboHeightVis">
<widget class="StyledComboBox" name="comboHeightVis">
<item>
<property name="text">
<string>None</string>
@ -455,6 +455,11 @@
<extends>QLineEdit</extends>
<header>src/conditiondialog.h</header>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>

View File

@ -126,7 +126,7 @@ Applies only to feature-structures of region-size = 32 and chunk-gap = 8, in par
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboLow20">
<widget class="StyledComboBox" name="comboLow20">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
@ -443,6 +443,11 @@ Applies only to feature-structures of region-size = 32 and chunk-gap = 8, in par
<extends>QLineEdit</extends>
<header>src/conditiondialog.h</header>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -83,7 +83,7 @@ QPushButton:hover {
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QComboBox" name="comboSearchType">
<widget class="StyledComboBox" name="comboSearchType">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
@ -242,6 +242,13 @@ QPushButton:hover {
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>
</resources>

View File

@ -11,7 +11,7 @@
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QComboBox" name="comboBiomes">
<widget class="StyledComboBox" name="comboBiomes">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -38,7 +38,7 @@
</widget>
</item>
<item row="8" column="1">
<widget class="QComboBox" name="comboNoiseW">
<widget class="StyledComboBox" name="comboNoiseW">
<property name="iconSize">
<size>
<width>0</width>
@ -69,7 +69,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboNoiseT">
<widget class="StyledComboBox" name="comboNoiseT">
<property name="iconSize">
<size>
<width>0</width>
@ -103,7 +103,7 @@
</widget>
</item>
<item row="14" column="1">
<widget class="QComboBox" name="comboHeight">
<widget class="StyledComboBox" name="comboHeight">
<property name="iconSize">
<size>
<width>0</width>
@ -120,7 +120,7 @@
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboNoiseH">
<widget class="StyledComboBox" name="comboNoiseH">
<property name="iconSize">
<size>
<width>0</width>
@ -165,7 +165,7 @@
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="comboNoiseC">
<widget class="StyledComboBox" name="comboNoiseC">
<property name="iconSize">
<size>
<width>0</width>
@ -175,7 +175,7 @@
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="comboNoiseE">
<widget class="StyledComboBox" name="comboNoiseE">
<property name="iconSize">
<size>
<width>0</width>
@ -207,6 +207,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>
</resources>

View File

@ -9,7 +9,6 @@
#include "maptoolsdialog.h"
#include "exportdialog.h"
#include "layerdialog.h"
#include "tabtriggers.h"
#include "tablocations.h"
#include "tabbiomes.h"
#include "tabstructures.h"
@ -88,7 +87,6 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent
ui->menuHistory->clear();
//ui->tabContainerSearch->addTab(new TabTriggers(this), tr("Triggers"));
ui->tabContainerSearch->addTab(new TabLocations(this), tr("Locations"));
ui->tabContainer->addTab(new TabBiomes(this), tr("Biomes"));
ui->tabContainer->addTab(new TabStructures(this), tr("Structures"));
@ -187,7 +185,7 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent
saction[D_GRID]->setChecked(true);
ui->splitterMap->setSizes(QList<int>({6800, 10000}));
ui->splitterMap->setSizes(QList<int>({7000, 10000}));
ui->splitterSearch->setSizes(QList<int>({1000, 3000}));
ui->splitterSeeds->setSizes(QList<int>({500, 2500}));

View File

@ -84,7 +84,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxMC">
<widget class="StyledComboBox" name="comboBoxMC">
<property name="toolTip">
<string>Minecraft version</string>
</property>
@ -113,7 +113,7 @@
</widget>
</item>
<item row="1" column="6">
<widget class="QComboBox" name="comboY">
<widget class="StyledComboBox" name="comboY">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -758,9 +758,14 @@ QToolButton:checked {
<customwidget>
<class>Collapsible</class>
<extends>QWidget</extends>
<header>src/collapsible.h</header>
<header>src/widgets.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../rc/icons.qrc"/>

View File

@ -556,7 +556,6 @@ void MapView::resizeEvent(QResizeEvent *e)
overlay->resize(width(), height());
}
void MapView::wheelEvent(QWheelEvent *e)
{
const qreal ang = e->angleDelta().y() / 8; // e->delta() / 8;

View File

@ -126,6 +126,11 @@ PresetDialog::PresetDialog(QWidget *parent, WorldInfo wi, bool showEamples)
"Looks for a suitable climate that primarily supports Jungle variants."),
wi.mc >= MC_1_18);
addPreset(":/examples/sinkhole.txt",
tr("Likely sinkhole (1.18+)"),
tr("Extreme climate weirdness that can generate holes to the world floor.\n\n"
"In versions 1.19 - 1.19.2, the world generation can have interesting artifacts at these places."),
wi.mc >= MC_1_18);
/*
addPreset(":/examples/old_growth_taiga_somewhere.txt",
tr("Large Old Growth Taiga somewhere (1.18+)"),

View File

@ -1,256 +0,0 @@
#include "rangeslider.h"
#include <QStyleOptionSlider>
#include <QPainter>
#include <QMouseEvent>
#include <QBoxLayout>
#include <QApplication>
RangeSlider::RangeSlider(QWidget *parent, int vmin, int vmax)
: QSlider(parent)
, vmin(vmin)
, vmax(vmax)
, pos0(vmin)
, pos1(vmax)
, holding(0)
{
setRange(vmin, vmax);
setOrientation(Qt::Horizontal);
// drawComplexControl() draws the background on every call if there is a stylesheet
// to mitigate this, we set the background transparent
setStyleSheet(
"QSlider { background-color: rgba(180, 180, 180, 0); }\n"
"QSlider::sub-page { background-color: rgba(255, 255, 255, 0); }"
);
//colinner = palette().color(QPalette::Highlight);
//colouter = QColor(0, 0, 0, 0);
}
RangeSlider::~RangeSlider()
{
}
void RangeSlider::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QStyleOptionSlider opt;
initStyleOption(&opt);
// draw the back groove of the slider without range highlighting
opt.subControls = QStyle::SC_SliderGroove;
opt.sliderValue = opt.sliderPosition = vmin;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
// draw the range highlighting between the handles
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
opt.sliderValue = opt.sliderPosition = pos0;
QRect handle0 = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
opt.sliderValue = opt.sliderPosition = pos1;
QRect handle1 = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (colinner.isValid())
{
painter.setBrush(QBrush(colinner));
painter.setPen(QPen(colinner, 0));
int x0 = handle0.center().x();
int x1 = handle1.center().x();
int y = handle0.center().y() - 1;
QRect span = QRect(x0, y, x1-x0, 3);
painter.drawRect(groove.intersected(span));
}
if (colouter.isValid())
{
painter.setBrush(QBrush(colouter));
painter.setPen(QPen(colouter, 0));
int x0 = handle0.center().x();
int x1 = handle1.center().x();
int y = handle0.center().y() - 1;
QRect left = QRect(groove.x()+1, y, x0-groove.x()-2, 3);
painter.drawRect(groove.intersected(left));
QRect right = QRect(x1+1, y, groove.right()-x1-2, 3);
painter.drawRect(groove.intersected(right));
}
int pmin = pos0 < pos1 ? pos0 : pos1;
int pmax = pos0 > pos1 ? pos0 : pos1;
// draw handles
opt.subControls = QStyle::SC_SliderHandle;
opt.sliderValue = opt.sliderPosition = pmax;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
opt.sliderValue = opt.sliderPosition = pmin;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
}
void RangeSlider::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
{
// determine which handle we are holding if any
QStyleOptionSlider opt;
initStyleOption(&opt);
QStyle::SubControl hit;
holding = 0;
opt.sliderValue = opt.sliderPosition = pos0;
hit = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, e->pos(), this);
if (hit == QStyle::SC_SliderHandle)
holding = -1;
opt.sliderValue = opt.sliderPosition = pos1;
hit = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, e->pos(), this);
if (hit == QStyle::SC_SliderHandle)
holding = +1;
}
QSlider::mousePressEvent(e);
}
void RangeSlider::mouseReleaseEvent(QMouseEvent *e)
{
holding = 0;
if (pos0 > pos1)
{
int tmp = pos0;
pos0 = pos1;
pos1 = tmp;
}
emit valueChanged(0);
QSlider::mouseReleaseEvent(e);
}
void RangeSlider::wheelEvent(QWheelEvent *e)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
int delta = e->angleDelta().y() / 8 / 15;
int x;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
x = e->x() - groove.x();
#else
x = (int)e->position().x() - groove.x();
#endif
x = style()->sliderValueFromPosition(vmin, vmax, x, groove.width());
int h = 0;
if (x < pos0)
h = -1;
else if (x > pos1)
h = +1;
else if (abs(pos0 - x) < abs(pos1 - x))
h = -1;
else if (abs(pos0 - x) > abs(pos1 - x))
h = +1;
if (e->modifiers() & Qt::ControlModifier)
delta *= 50;
if (h < 0)
{
pos0 += delta;
if (pos0 < vmin) pos0 = vmin;
if (pos0 > pos1) pos0 = pos1;
emit valueChanged(pos0);
}
else
{
pos1 += delta;
if (pos1 > vmax) pos1 = vmax;
if (pos1 < pos0) pos1 = pos0;
emit valueChanged(pos1);
}
update();
}
void RangeSlider::mouseMoveEvent(QMouseEvent *e)
{
if (holding == 0)
return;
QStyleOptionSlider opt;
initStyleOption(&opt);
opt.sliderValue = opt.sliderPosition = holding < 0 ? pos0 : pos1;
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
int x = e->x() - groove.x();
x = style()->sliderValueFromPosition(vmin, vmax, x, groove.width());
if (holding < 0)
pos0 = x;
else
pos1 = x;
emit valueChanged(x);
update();
}
LabeledRange::LabeledRange(QWidget *parent, int vmin, int vmax)
: QWidget(parent)
{
slider = new RangeSlider(this, vmin, vmax);
minlabel = new QLabel(this);
maxlabel = new QLabel(this);
minlabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
maxlabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
int w = fontMetrics().tightBoundingRect("+012345").width();
minlabel->setFixedWidth(w);
maxlabel->setFixedWidth(w);
QBoxLayout *l = new QBoxLayout(QBoxLayout::LeftToRight, this);
l->addWidget(minlabel);
l->addWidget(slider);
l->addWidget(maxlabel);
l->setContentsMargins(0, 0, 0, 0);
setLayout(l);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(rangeChanged(void)));
rangeChanged();
}
LabeledRange::~LabeledRange()
{
}
void LabeledRange::setLimitText(QString min, QString max)
{
mintxt = min;
maxtxt = max;
rangeChanged();
}
void LabeledRange::setValues(int p0, int p1)
{
slider->pos0 = p0;
slider->pos1 = p1;
slider->update();
rangeChanged();
}
void LabeledRange::setHighlight(QColor inner, QColor outer)
{
slider->colinner = inner;
slider->colouter = outer;
slider->update();
}
void LabeledRange::rangeChanged()
{
int pmin = slider->pos0 < slider->pos1 ? slider->pos0 : slider->pos1;
int pmax = slider->pos0 > slider->pos1 ? slider->pos0 : slider->pos1;
minlabel->setText(QString::number(pmin));
maxlabel->setText(QString::number(pmax));
if (!mintxt.isEmpty() && pmin == slider->vmin)
minlabel->setText(mintxt);
if (!maxtxt.isEmpty() && pmax == slider->vmax)
maxlabel->setText(maxtxt);
emit onRangeChange();
}

View File

@ -1,50 +0,0 @@
#ifndef RANGESLIDER_H
#define RANGESLIDER_H
#include <QSlider>
#include <QLabel>
class RangeSlider : public QSlider
{
Q_OBJECT
public:
RangeSlider(QWidget *parent = nullptr, int vmin = 0, int vmax = 100);
virtual ~RangeSlider();
virtual void paintEvent(QPaintEvent *e) override;
virtual void wheelEvent(QWheelEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
virtual void mouseMoveEvent(QMouseEvent *e) override;
int vmin, vmax;
int pos0, pos1;
int holding;
QColor colinner, colouter;
};
class LabeledRange : public QWidget
{
Q_OBJECT
public:
LabeledRange(QWidget *parent = nullptr, int vmin = 0, int vmax = 10);
virtual ~LabeledRange();
void setValues(int p0, int p1);
void setLimitText(QString min, QString max);
void setHighlight(QColor inner, QColor outer);
public slots:
void rangeChanged();
signals:
void onRangeChange();
public:
RangeSlider *slider;
QLabel *minlabel, *maxlabel;
QString mintxt, maxtxt;
};
#endif // RANGESLIDER_H

View File

@ -439,7 +439,6 @@ BlockRule *LuaHighlighter::nextBlockRule(const QString& text, int *pos, int *nex
int end = 0;
for (int i = 0, n = blockrules.size(); i < n; i++)
{
#if 1
QRegularExpressionMatch m = blockrules[i].start.match(text, *pos);
if (m.hasMatch() && m.capturedStart() < min)
{
@ -448,16 +447,6 @@ BlockRule *LuaHighlighter::nextBlockRule(const QString& text, int *pos, int *nex
end = m.capturedEnd();
}
}
#else
int s = blockrules[i].start.indexIn(text, *pos);
if (s >= 0 && s < min)
{
rule = &blockrules[i];
min = s;
end = s + rule->start.matchedLength();
}
}
#endif
if (rule)
{
*pos = min;
@ -480,7 +469,6 @@ void LuaHighlighter::highlightBlock(const QString& text)
while (rule)
{
#if 1
QRegularExpressionMatch m = rule->end.match(text, match);
if (m.hasMatch())
{
@ -494,21 +482,6 @@ void LuaHighlighter::highlightBlock(const QString& text)
start = end;
rule = nextBlockRule(line, &start, &match);
}
#else
int end = rule->end.indexIn(text, match);
if (end == -1)
{
markFormated(&line, start, line.length() - start, rule->format);
break;
}
else
{
end += rule->end.matchedLength();
markFormated(&line, start, end - start, rule->format);
start = end;
rule = nextBlockRule(line, &start, &match);
}
#endif
}
if (rule)
setCurrentBlockState(rule - &blockrules[0]);
@ -518,23 +491,12 @@ void LuaHighlighter::highlightBlock(const QString& text)
for (const Rule &rule : qAsConst(rules))
{
const QString *l = rule.overlay ? &text : &line;
#if 1
QRegularExpressionMatch m = rule.pattern.match(*l);
while (m.hasMatch())
{
setFormat(m.capturedStart(), m.capturedLength(), rule.format);
m = rule.pattern.match(*l, m.capturedEnd());
}
#else
QRegExp expression(rule.pattern);
int index = expression.indexIn(*l);
while (index >= 0)
{
int length = expression.matchedLength();
setFormat(index, length, rule.format);
index = expression.indexIn(*l, index + length);
}
#endif
}
}

View File

@ -42,7 +42,7 @@
</layout>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboSeedSource">
<widget class="StyledComboBox" name="comboSeedSource">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -285,7 +285,7 @@
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboBiome">
<widget class="StyledComboBox" name="comboBiome">
<property name="editable">
<bool>true</bool>
</property>
@ -356,7 +356,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboScale">
<widget class="StyledComboBox" name="comboScale">
<property name="currentIndex">
<number>1</number>
</property>
@ -426,6 +426,11 @@
<extends>QLineEdit</extends>
<header>src/conditiondialog.h</header>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -94,7 +94,7 @@ void AnalysisLocations::run()
double dist = sqrt((double)at.x*at.x + (double)at.z*at.z);
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, tr("at"));
item->setText(0, tr("@"));
item->setText(1, QString::asprintf("%" PRId64, seed));
item->setText(2, QString::number(at.x));
item->setText(3, QString::number(at.z));

View File

@ -78,7 +78,7 @@
</layout>
</item>
<item row="1" column="1" colspan="4">
<widget class="QComboBox" name="comboSampling">
<widget class="StyledComboBox" name="comboSampling">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -193,7 +193,7 @@
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QComboBox" name="comboSeedSource">
<widget class="StyledComboBox" name="comboSeedSource">
<item>
<property name="text">
<string>Current seed</string>
@ -208,6 +208,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -72,6 +72,9 @@ void AnalysisStructures::runStructs(Generator *g)
sdim = DIM_NETHER;
if (sconf.properties & STRUCT_END)
sdim = DIM_END;
if (dim != DIM_UNDEF && dim != sdim)
continue;
getStructs(&st, sconf, wi, sdim, area.x1, area.z1, area.x2, area.z2);
if (st.empty())
continue;
@ -99,7 +102,7 @@ void AnalysisStructures::runStructs(Generator *g)
}
}
if (!stop && mapshow[D_SPAWN])
if (!stop && mapshow[D_SPAWN] && (dim == DIM_UNDEF || dim == DIM_OVERWORLD))
{
applySeed(g, 0, wi.seed);
Pos pos = getSpawn(g);
@ -117,7 +120,7 @@ void AnalysisStructures::runStructs(Generator *g)
}
}
if (!stop && mapshow[D_STRONGHOLD])
if (!stop && mapshow[D_STRONGHOLD] && (dim == DIM_UNDEF || dim == DIM_OVERWORLD))
{
StrongholdIter sh;
initFirstStronghold(&sh, wi.mc, wi.seed);
@ -444,8 +447,18 @@ void TabStructures::on_pushStart_clicked()
thread.collect = ui->checkCollect->isChecked();
if (ui->radioMap->isChecked())
{
for (int sopt = 0; sopt < D_STRUCT_NUM; sopt++)
thread.mapshow[sopt] = ui->radioAll->isChecked() || parent->getMapView()->getShow(sopt);
thread.mapshow[sopt] = parent->getMapView()->getShow(sopt);
thread.dim = parent->getDim();
}
else
{
for (int sopt = 0; sopt < D_STRUCT_NUM; sopt++)
thread.mapshow[sopt] = true;
thread.dim = DIM_UNDEF;
}
if (ui->tabWidget->currentWidget() == ui->tabStructures)
{

View File

@ -26,6 +26,7 @@ signals:
public:
std::vector<uint64_t> seeds;
WorldInfo wi;
int dim;
std::atomic_bool stop;
std::atomic_int idx;
struct Dat { int x1, z1, x2, z2; } area;

View File

@ -7,7 +7,7 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QComboBox" name="comboSeedSource">
<widget class="StyledComboBox" name="comboSeedSource">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -339,6 +339,11 @@
<extends>QLineEdit</extends>
<header>src/conditiondialog.h</header>
</customwidget>
<customwidget>
<class>StyledComboBox</class>
<extends>QComboBox</extends>
<header>src/widgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -1,314 +0,0 @@
#include "tabtriggers.h"
#include "ui_tabtriggers.h"
#include "message.h"
#include "util.h"
#include "config.h"
#include <QElapsedTimer>
#include <QFileDialog>
#include <QTextStream>
#include <QFileInfo>
#include <vector>
static
QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, int64_t seed, Pos cpos[], QTreeWidgetItem* parent, bool posval)
{
Condition& c = ctree.condvec[node];
Pos p = cpos[c.save];
const std::vector<char>& branches = ctree.references[c.save];
QTreeWidgetItem* item;
if (c.type == 0)
{
item = parent;
}
else
{
item = new QTreeWidgetItem(parent);
item->setText(0, "-");
item->setText(1, c.summary(false));
if ((p.x == -1 && p.z == -1) || c.type == F_LOGIC_NOT)
posval = false;
if (posval)
{
const FilterInfo& finfo = g_filterinfo.list[c.type];
item->setText(2, QString::number(p.x));
item->setText(3, QString::number(p.z));
item->setData(0, Qt::UserRole+0, QVariant::fromValue(seed));
item->setData(0, Qt::UserRole+1, QVariant::fromValue(finfo.dim));
item->setData(0, Qt::UserRole+2, QVariant::fromValue(p));
}
}
if (!branches.empty())
{
for (char b : branches)
setConditionTreeItems(ctree, b, seed, cpos, item, posval);
}
return item;
}
void AnalysisTriggers::run()
{
stop = false;
for (idx = 0; idx < (long)seeds.size(); idx++)
{
if (stop) break;
int64_t seed = seeds[idx];
wi.seed = seed;
QTreeWidgetItem *seeditem = new QTreeWidgetItem();
seeditem->setText(0, QString::asprintf("%" PRId64, seed));
seeditem->setData(0, Qt::UserRole, QVariant::fromValue(seed));
if (!conds.empty())
{
ConditionTree condtree;
SearchThreadEnv env;
QString err = condtree.set(conds, wi.mc);
if (err.isEmpty())
err = env.init(wi.mc, wi.large, &condtree);
if (!err.isEmpty())
{
delete seeditem;
emit warning(err, QMessageBox::Ok);
break;
}
env.setSeed(wi.seed);
Pos origin = {0, 0};
Pos cpos[MAX_INSTANCES] = {};
if (testTreeAt(origin, &env, PASS_FULL_64, &stop, cpos)
== COND_OK)
{
setConditionTreeItems(condtree, 0, seed, cpos, seeditem, true);
}
}
if (seeditem->childCount() == 0)
{
delete seeditem;
continue;
}
if (stop)
seeditem->setText(0, QString::asprintf("%" PRId64, seed) + " " + tr("(incomplete)"));
emit itemDone(seeditem);
}
}
TabTriggers::TabTriggers(MainWindow *parent)
: QWidget(parent)
, ui(new Ui::TabTriggers)
, parent(parent)
, thread()
, nextupdate()
, updt(20)
{
ui->setupUi(this);
ui->treeWidget->setSortingEnabled(false); // sortable triggers are not necessary
connect(&thread, &AnalysisTriggers::warning, this, &TabTriggers::warning, Qt::BlockingQueuedConnection);
connect(&thread, &AnalysisTriggers::itemDone, this, &TabTriggers::onAnalysisItemDone, Qt::BlockingQueuedConnection);
connect(&thread, &AnalysisTriggers::finished, this, &TabTriggers::onAnalysisFinished);
}
TabTriggers::~TabTriggers()
{
thread.stop = true;
thread.wait(500);
delete ui;
}
bool TabTriggers::event(QEvent *e)
{
if (e->type() == QEvent::LayoutRequest)
{
QFontMetrics fm = QFontMetrics(ui->treeWidget->font());
ui->treeWidget->setColumnWidth(0, txtWidth(fm) * 24);
ui->treeWidget->setColumnWidth(1, txtWidth(fm) * 30);
ui->treeWidget->setColumnWidth(2, txtWidth(fm) * 9);
ui->treeWidget->setColumnWidth(3, txtWidth(fm) * 9);
}
return QWidget::event(e);
}
void TabTriggers::save(QSettings&)
{
}
void TabTriggers::load(QSettings&)
{
}
int TabTriggers::warning(QString text, QMessageBox::StandardButtons buttons)
{
return warn(parent, text, buttons);
}
void TabTriggers::onAnalysisItemDone(QTreeWidgetItem *item)
{
qbuf.push_back(item);
quint64 ns = elapsed.nsecsElapsed();
if (ns > nextupdate)
{
nextupdate = ns + updt * 1e6;
QTimer::singleShot(updt, this, &TabTriggers::onBufferTimeout);
}
}
void TabTriggers::onAnalysisFinished()
{
onBufferTimeout();
ui->pushExport->setEnabled(ui->treeWidget->topLevelItemCount() > 0);
ui->pushStart->setChecked(false);
ui->pushStart->setText(tr("Analyze"));
}
void TabTriggers::onBufferTimeout()
{
uint64_t t = -elapsed.elapsed();
if (!qbuf.empty())
{
ui->treeWidget->setUpdatesEnabled(false);
ui->treeWidget->addTopLevelItems(qbuf);
ui->treeWidget->setUpdatesEnabled(true);
QString progress = QString::asprintf(" (%ld/%zu)", thread.idx.load(), thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
qbuf.clear();
}
QApplication::processEvents(); // force processing of events so we can time correctly
t += elapsed.elapsed();
if (8*t > updt)
updt = 4*t;
nextupdate = elapsed.nsecsElapsed() + 1e6 * updt;
}
void TabTriggers::on_pushStart_clicked()
{
if (thread.isRunning())
{
thread.stop = true;
return;
}
updt = 20;
nextupdate = 0;
elapsed.start();
parent->getSeed(&thread.wi);
thread.conds = parent->formCond->getConditions();
thread.seeds.clear();
if (ui->comboSeedSource->currentIndex() == 0)
thread.seeds.push_back(thread.wi.seed);
else
thread.seeds = parent->formControl->getResults();
//ui->treeWidget->setSortingEnabled(false);
while (ui->treeWidget->topLevelItemCount() > 0)
delete ui->treeWidget->takeTopLevelItem(0);
ui->pushExport->setEnabled(false);
ui->pushStart->setChecked(true);
QString progress = QString::asprintf(" (0/%zu)", thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
thread.start();
}
void TabTriggers::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
{
(void) column;
QVariant dat;
dat = item->data(0, Qt::UserRole+0);
if (dat.isValid())
{
uint64_t seed = qvariant_cast<uint64_t>(dat);
dat = item->data(0, Qt::UserRole+1);
int dim = dat.isValid() ? dat.toInt() : DIM_UNDEF;
WorldInfo wi;
parent->getSeed(&wi);
wi.seed = seed;
parent->setSeed(wi, dim);
}
dat = item->data(0, Qt::UserRole+2);
if (dat.isValid())
{
Pos p = qvariant_cast<Pos>(dat);
parent->getMapView()->setView(p.x+0.5, p.z+0.5);
}
}
void TabTriggers::on_pushExpand_clicked()
{
bool expand = false;
for (QTreeWidgetItemIterator it(ui->treeWidget); *it; ++it)
if (!(*it)->isExpanded())
expand = true;
if (expand)
ui->treeWidget->expandAll();
else
ui->treeWidget->collapseAll();
}
static
void csvline(QTextStream& stream, const QString& qte, const QString& sep, QStringList& cols)
{
if (qte.isEmpty())
{
for (QString& s : cols)
if (s.contains(sep))
s = "\"" + s + "\"";
}
stream << qte << cols.join(sep) << qte << "\n";
}
void TabTriggers::on_pushExport_clicked()
{
QString fnam = QFileDialog::getSaveFileName(
this, tr("Export trigger analysis"), parent->prevdir, tr("Text files (*.txt *csv);;Any files (*)"));
if (fnam.isEmpty())
return;
QFileInfo finfo(fnam);
QFile file(fnam);
parent->prevdir = finfo.absolutePath();
if (!file.open(QIODevice::WriteOnly))
{
warn(parent, tr("Failed to open file for export:\n\"%1\"").arg(fnam));
return;
}
QString qte = parent->config.quote;
QString sep = parent->config.separator;
QTextStream stream(&file);
stream << "Sep=" + sep + "\n";
sep = qte + sep + qte;
QStringList header = { tr("seed"), tr("condition"), tr("x"), tr("z") };
csvline(stream, qte, sep, header);
QTreeWidgetItemIterator it(ui->treeWidget);
for (; *it; ++it)
{
QTreeWidgetItem *item = *it;
QStringList cols;
for (int i = 0, n = item->columnCount(); i < n; i++)
{
QString s = item->text(i);
if (s == "-") s = "";
cols.append(s);
}
csvline(stream, qte, sep, cols);
}
}

View File

@ -1,73 +0,0 @@
#ifndef TABTRIGGERS_H
#define TABTRIGGERS_H
#include <QWidget>
#include <QThread>
#include <QTreeWidgetItem>
#include "mainwindow.h"
#include "search.h"
#include "world.h"
namespace Ui {
class TabTriggers;
}
class AnalysisTriggers : public QThread
{
Q_OBJECT
public:
explicit AnalysisTriggers(QObject *parent = nullptr)
: QThread(parent), conds(),seeds(),wi(),stop(),idx() {}
virtual void run() override;
signals:
void itemDone(QTreeWidgetItem *item);
int warning(QString text, QMessageBox::StandardButtons buttons);
public:
QVector<Condition> conds;
std::vector<uint64_t> seeds;
WorldInfo wi;
std::atomic_bool stop;
std::atomic_long idx;
};
class TabTriggers : public QWidget, public ISaveTab
{
Q_OBJECT
public:
explicit TabTriggers(MainWindow *parent);
~TabTriggers();
virtual bool event(QEvent *e) override;
virtual void save(QSettings& settings) override;
virtual void load(QSettings& settings) override;
private slots:
int warning(QString text, QMessageBox::StandardButtons buttons);
void onAnalysisItemDone(QTreeWidgetItem *item);
void onAnalysisFinished();
void onBufferTimeout();
void on_pushStart_clicked();
void on_pushExpand_clicked();
void on_pushExport_clicked();
void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column);
private:
Ui::TabTriggers *ui;
MainWindow *parent;
AnalysisTriggers thread;
QElapsedTimer elapsed;
uint64_t nextupdate;
uint64_t updt;
QList<QTreeWidgetItem*> qbuf;
};
#endif // TABTRIGGERS_H

View File

@ -1,142 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabTriggers</class>
<widget class="QWidget" name="TabTriggers">
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QComboBox" name="comboSeedSource">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Current seed</string>
</property>
</item>
<item>
<property name="text">
<string>From matching seeds list</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Seed(s):</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="labelDescription">
<property name="text">
<string>Examine how the conditions are evaluated.</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QTreeWidget" name="treeWidget">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
<property name="indentation">
<number>12</number>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerDefaultSectionSize">
<number>100</number>
</attribute>
<column>
<property name="text">
<string>seed</string>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
</column>
<column>
<property name="text">
<string>condition</string>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
</column>
<column>
<property name="text">
<string>x</string>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
</column>
<column>
<property name="text">
<string>z</string>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
</column>
</widget>
</item>
<item row="6" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushExport">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Export...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushExpand">
<property name="text">
<string>Expand all</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushStart">
<property name="text">
<string>Analyze</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

540
src/widgets.cpp Normal file
View File

@ -0,0 +1,540 @@
#include "widgets.h"
#include "util.h"
#include <QApplication>
#include <QStyleOptionSlider>
#include <QPainter>
#include <QMouseEvent>
#include <QBoxLayout>
#include <QPropertyAnimation>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFont>
#include <QPushButton>
#include <QMessageBox>
#include <QSpinBox>
#include <QLineEdit>
#include <QTextEdit>
#include <QParallelAnimationGroup>
#include <QToolButton>
#include <QFrame>
#include <QLabel>
#include <QListWidgetItem>
Collapsible::Collapsible(QWidget *parent)
: QWidget(parent)
, animgroup(new QParallelAnimationGroup(this))
, toggleButton(new QToolButton(this))
, frameBar(new QFrame(this))
, content()
, contentHeight()
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
QFont font = toggleButton->font();
font.setBold(true);
toggleButton->setFont(font);
toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
toggleButton->setCheckable(true);
frameBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
frameBar->setFrameShape(QFrame::HLine);
QVBoxLayout *vbox = new QVBoxLayout();
layoutBar = new QHBoxLayout();
layoutBar->addWidget(toggleButton, Qt::AlignBottom);
layoutBar->addWidget(frameBar, Qt::AlignCenter);
layoutBar->setSpacing(5);
vbox->addLayout(layoutBar);
layoutContent = new QHBoxLayout();
layoutContent->setContentsMargins(20, 0, 0, 0);
vbox->addLayout(layoutContent);
vbox->setSizeConstraint(QLayout::SetMaximumSize);
vbox->setContentsMargins(0, 0, 0, 8);
vbox->setSpacing(0);
setLayout(vbox);
connect(toggleButton, &QToolButton::toggled, this, &Collapsible::toggle);
connect(animgroup, &QAbstractAnimation::finished, this, &Collapsible::finishAnimation);
}
void Collapsible::toggle(bool collapsed)
{
if (!content)
return;
int height = content->size().height();
if (height)
contentHeight = height;
if (collapsed)
{
content->setMinimumHeight(0);
content->setMaximumHeight(0);
toggleButton->setArrowType(Qt::ArrowType::DownArrow);
animgroup->setDirection(QAbstractAnimation::Forward);
}
else
{
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
animgroup->setDirection(QAbstractAnimation::Backward);
}
for (int i = 0, n = animgroup->animationCount(); i < n; i++)
{
QPropertyAnimation *anim;
anim = (QPropertyAnimation*) animgroup->animationAt(i);
anim->setStartValue(0);
anim->setEndValue(contentHeight);
anim->setDuration(200);
}
animgroup->start();
}
void Collapsible::finishAnimation()
{
if (toggleButton->isChecked())
{
content->setMinimumHeight(0);
content->setMaximumHeight(16777215);
}
}
void Collapsible::init(const QString& title, QWidget *widget, bool collapsed)
{
toggleButton->setText(title);
layoutContent->addWidget(widget);
contentHeight = widget->sizeHint().height();
content = widget;
animgroup->addAnimation(new QPropertyAnimation(content, "minimumHeight"));
animgroup->addAnimation(new QPropertyAnimation(content, "maximumHeight"));
setCollapsed(collapsed);
}
void Collapsible::setCollapsed(bool collapsed)
{
if (collapsed)
{
toggleButton->setChecked(false);
content->setMinimumHeight(0);
content->setMaximumHeight(0);
toggleButton->setArrowType(Qt::ArrowType::RightArrow);
animgroup->setDirection(QAbstractAnimation::Backward);
}
else
{
toggleButton->setChecked(true);
content->setMinimumHeight(0);
content->setMaximumHeight(16777215);
toggleButton->setArrowType(Qt::ArrowType::DownArrow);
animgroup->setDirection(QAbstractAnimation::Forward);
}
animgroup->stop();
}
void Collapsible::setInfo(const QString& title, const QString& text)
{
QPushButton *button = new QPushButton("", this);
button->setStyleSheet(
"QPushButton {"
" background-color: rgba(255, 255, 255, 0);"
" border: none;"
" image: url(:/icons/info.png);"
" image-position: right;"
"}"
"QPushButton:hover {"
" image: url(:/icons/info_h.png);"
"}");
int fmh = fontMetrics().height();
button->setIconSize(QSize(fmh, fmh));
button->setToolTip(tr("Show help"));
connect(button, SIGNAL(clicked()), this, SLOT(showInfo()));
layoutBar->addWidget(button, Qt::AlignLeft);
infotitle = title;
infomsg = text;
}
void Collapsible::showInfo()
{
// windows plays an annoying sound when you use QMessageBox::information()
QMessageBox mb(this);
mb.setIcon(QMessageBox::Information);
mb.setWindowTitle(infotitle);
mb.setText(infomsg);
mb.exec();
}
RangeSlider::RangeSlider(QWidget *parent, int vmin, int vmax)
: QSlider(parent)
, vmin(vmin)
, vmax(vmax)
, pos0(vmin)
, pos1(vmax)
, holding(0)
{
setRange(vmin, vmax);
setOrientation(Qt::Horizontal);
// drawComplexControl() draws the background on every call if there is a stylesheet
// to mitigate this, we set the background transparent
setStyleSheet(
"QSlider { background-color: rgba(180, 180, 180, 0); }\n"
"QSlider::sub-page { background-color: rgba(255, 255, 255, 0); }"
);
//colinner = palette().color(QPalette::Highlight);
//colouter = QColor(0, 0, 0, 0);
}
RangeSlider::~RangeSlider()
{
}
void RangeSlider::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QStyleOptionSlider opt;
initStyleOption(&opt);
// draw the back groove of the slider without range highlighting
opt.subControls = QStyle::SC_SliderGroove;
opt.sliderValue = opt.sliderPosition = vmin;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
// draw the range highlighting between the handles
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
opt.sliderValue = opt.sliderPosition = pos0;
QRect handle0 = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
opt.sliderValue = opt.sliderPosition = pos1;
QRect handle1 = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (colinner.isValid())
{
painter.setBrush(QBrush(colinner));
painter.setPen(QPen(colinner, 0));
int x0 = handle0.center().x();
int x1 = handle1.center().x();
int y = handle0.center().y() - 1;
QRect span = QRect(x0, y, x1-x0, 3);
painter.drawRect(groove.intersected(span));
}
if (colouter.isValid())
{
painter.setBrush(QBrush(colouter));
painter.setPen(QPen(colouter, 0));
int x0 = handle0.center().x();
int x1 = handle1.center().x();
int y = handle0.center().y() - 1;
QRect left = QRect(groove.x()+1, y, x0-groove.x()-2, 3);
painter.drawRect(groove.intersected(left));
QRect right = QRect(x1+1, y, groove.right()-x1-2, 3);
painter.drawRect(groove.intersected(right));
}
int pmin = pos0 < pos1 ? pos0 : pos1;
int pmax = pos0 > pos1 ? pos0 : pos1;
// draw handles
opt.subControls = QStyle::SC_SliderHandle;
opt.sliderValue = opt.sliderPosition = pmax;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
opt.sliderValue = opt.sliderPosition = pmin;
style()->drawComplexControl(QStyle::CC_Slider, &opt, &painter, this);
}
void RangeSlider::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
{
// determine which handle we are holding if any
QStyleOptionSlider opt;
initStyleOption(&opt);
QStyle::SubControl hit;
holding = 0;
opt.sliderValue = opt.sliderPosition = pos0;
hit = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, e->pos(), this);
if (hit == QStyle::SC_SliderHandle)
holding = -1;
opt.sliderValue = opt.sliderPosition = pos1;
hit = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, e->pos(), this);
if (hit == QStyle::SC_SliderHandle)
holding = +1;
}
QSlider::mousePressEvent(e);
}
void RangeSlider::mouseReleaseEvent(QMouseEvent *e)
{
holding = 0;
if (pos0 > pos1)
{
int tmp = pos0;
pos0 = pos1;
pos1 = tmp;
}
emit valueChanged(0);
QSlider::mouseReleaseEvent(e);
}
void RangeSlider::wheelEvent(QWheelEvent *e)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
int delta = e->angleDelta().y() / 8 / 15;
int x;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
x = e->x() - groove.x();
#else
x = (int)e->position().x() - groove.x();
#endif
x = style()->sliderValueFromPosition(vmin, vmax, x, groove.width());
int h = 0;
if (x < pos0)
h = -1;
else if (x > pos1)
h = +1;
else if (abs(pos0 - x) < abs(pos1 - x))
h = -1;
else if (abs(pos0 - x) > abs(pos1 - x))
h = +1;
if (e->modifiers() & Qt::ControlModifier)
delta *= 50;
if (h < 0)
{
pos0 += delta;
if (pos0 < vmin) pos0 = vmin;
if (pos0 > pos1) pos0 = pos1;
emit valueChanged(pos0);
}
else
{
pos1 += delta;
if (pos1 > vmax) pos1 = vmax;
if (pos1 < pos0) pos1 = pos0;
emit valueChanged(pos1);
}
update();
}
void RangeSlider::mouseMoveEvent(QMouseEvent *e)
{
if (holding == 0)
return;
QStyleOptionSlider opt;
initStyleOption(&opt);
opt.sliderValue = opt.sliderPosition = holding < 0 ? pos0 : pos1;
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
int x = e->x() - groove.x();
x = style()->sliderValueFromPosition(vmin, vmax, x, groove.width());
if (holding < 0)
pos0 = x;
else
pos1 = x;
emit valueChanged(x);
update();
}
LabeledRange::LabeledRange(QWidget *parent, int vmin, int vmax)
: QWidget(parent)
{
slider = new RangeSlider(this, vmin, vmax);
minlabel = new QLabel(this);
maxlabel = new QLabel(this);
minlabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
maxlabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
int w = fontMetrics().tightBoundingRect("+012345").width();
minlabel->setFixedWidth(w);
maxlabel->setFixedWidth(w);
QBoxLayout *l = new QBoxLayout(QBoxLayout::LeftToRight, this);
l->addWidget(minlabel);
l->addWidget(slider);
l->addWidget(maxlabel);
l->setContentsMargins(0, 0, 0, 0);
setLayout(l);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(rangeChanged(void)));
rangeChanged();
}
LabeledRange::~LabeledRange()
{
}
void LabeledRange::setLimitText(QString min, QString max)
{
mintxt = min;
maxtxt = max;
rangeChanged();
}
void LabeledRange::setValues(int p0, int p1)
{
slider->pos0 = p0;
slider->pos1 = p1;
slider->update();
rangeChanged();
}
void LabeledRange::setHighlight(QColor inner, QColor outer)
{
slider->colinner = inner;
slider->colouter = outer;
slider->update();
}
void LabeledRange::rangeChanged()
{
int pmin = slider->pos0 < slider->pos1 ? slider->pos0 : slider->pos1;
int pmax = slider->pos0 > slider->pos1 ? slider->pos0 : slider->pos1;
minlabel->setText(QString::number(pmin));
maxlabel->setText(QString::number(pmax));
if (!mintxt.isEmpty() && pmin == slider->vmin)
minlabel->setText(mintxt);
if (!maxtxt.isEmpty() && pmax == slider->vmax)
maxlabel->setText(maxtxt);
emit onRangeChange();
}
QSize CoordEdit::sizeHint() const
{
QSize size = QLineEdit::minimumSizeHint();
QFontMetrics fm(font());
size.setWidth(size.width() + txtWidth(fm, "-30000000"));
return size;
}
SpinExclude::SpinExclude(QWidget *parent)
: QSpinBox(parent)
{
setMinimum(-1);
QObject::connect(this, SIGNAL(valueChanged(int)), this, SLOT(change(int)), Qt::QueuedConnection);
}
SpinExclude::~SpinExclude()
{
}
int SpinExclude::valueFromText(const QString &text) const
{
return QSpinBox::valueFromText(text.section(" ", 0, 0));
}
QString SpinExclude::textFromValue(int value) const
{
QString txt = QSpinBox::textFromValue(value);
if (value == 0)
txt += " " + tr("(ignore)");
if (value == -1)
txt += " " + tr("(exclude)");
return txt;
}
void SpinExclude::change(int v)
{
const char *style = "";
if (v < 0)
style = "background: #28ff0000";
if (v > 0)
style = "background: #2800ff00";
setStyleSheet(style);
findChild<QLineEdit*>()->deselect();
}
SpinInstances::SpinInstances(QWidget *parent)
: QSpinBox(parent)
{
setRange(0, 99);
}
SpinInstances::~SpinInstances()
{
}
int SpinInstances::valueFromText(const QString &text) const
{
return QSpinBox::valueFromText(text.section(" ", 0, 0));
}
QString SpinInstances::textFromValue(int value) const
{
QString txt = QSpinBox::textFromValue(value);
if (value == 0)
txt += " " + tr("(exclude)");
if (value > 1)
txt += " " + tr("(cluster)");
return txt;
}
ComboBoxDelegate::ComboBoxDelegate(QObject *parent, QComboBox *cmb)
: QStyledItemDelegate(parent)
, combo(cmb)
{
}
ComboBoxDelegate::~ComboBoxDelegate()
{
}
static bool isSeparator(const QModelIndex &index)
{
return index.data(Qt::AccessibleDescriptionRole).toString() == QString::fromLatin1("separator");
}
void ComboBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (isSeparator(index))
{
QStyleOptionViewItem opt = option;
if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget))
opt.rect.setWidth(view->viewport()->width());
combo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, combo);
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
QSize ComboBoxDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (isSeparator(index))
{
int pm = combo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, combo) + 4;
return QSize(pm, pm);
}
QSize size = QStyledItemDelegate::sizeHint(option, index);
int h = size.height();
int hicon = combo->iconSize().height() + 1;
int hfont = combo->fontMetrics().height() + 4;
if (h < hicon) h = hicon;
if (h < hfont) h = hfont;
if (size.height() < h)
size.setHeight(h);
return size;
}

143
src/widgets.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef WIDGETS_H
#define WIDGETS_H
#include <QWidget>
#include <QSlider>
#include <QComboBox>
#include <QStyledItemDelegate>
#include <QSpinBox>
#include <QLineEdit>
class QParallelAnimationGroup;
class QToolButton;
class QFrame;
class QHBoxLayout;
class QLabel;
class Collapsible : public QWidget
{
Q_OBJECT
public:
explicit Collapsible(QWidget *parent = nullptr);
void init(const QString& title, QWidget *widget, bool collapsed);
void setInfo(const QString& title, const QString& text);
void setCollapsed(bool collapsed); // without animation
public slots:
void toggle(bool collapsed);
void finishAnimation();
void showInfo();
public:
QParallelAnimationGroup* animgroup;
QToolButton* toggleButton;
QFrame* frameBar;
QWidget *content;
QHBoxLayout *layoutBar;
QHBoxLayout *layoutContent;
int contentHeight;
QString infotitle;
QString infomsg;
};
class RangeSlider : public QSlider
{
Q_OBJECT
public:
RangeSlider(QWidget *parent = nullptr, int vmin = 0, int vmax = 100);
virtual ~RangeSlider();
virtual void paintEvent(QPaintEvent *e) override;
virtual void wheelEvent(QWheelEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
virtual void mouseMoveEvent(QMouseEvent *e) override;
int vmin, vmax;
int pos0, pos1;
int holding;
QColor colinner, colouter;
};
class LabeledRange : public QWidget
{
Q_OBJECT
public:
LabeledRange(QWidget *parent = nullptr, int vmin = 0, int vmax = 10);
virtual ~LabeledRange();
void setValues(int p0, int p1);
void setLimitText(QString min, QString max);
void setHighlight(QColor inner, QColor outer);
public slots:
void rangeChanged();
signals:
void onRangeChange();
public:
RangeSlider *slider;
QLabel *minlabel, *maxlabel;
QString mintxt, maxtxt;
};
// QLineEdit defaults to a style hint width 17 characters, which is too long for coordinates
class CoordEdit : public QLineEdit
{
Q_OBJECT
public:
CoordEdit(QWidget *parent = nullptr) : QLineEdit(parent) {}
virtual QSize sizeHint() const override;
};
class SpinExclude : public QSpinBox
{
Q_OBJECT
public:
SpinExclude(QWidget *parent = nullptr);
virtual ~SpinExclude();
virtual int valueFromText(const QString &text) const override;
virtual QString textFromValue(int value) const override;
public slots:
void change(int v);
};
class SpinInstances : public QSpinBox
{
Q_OBJECT
public:
SpinInstances(QWidget *parent = nullptr);
virtual ~SpinInstances();
virtual int valueFromText(const QString &text) const override;
virtual QString textFromValue(int value) const override;
};
// QComboBox uses QItemDelegate, which would not support styles
class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent, QComboBox *cmb);
virtual ~ComboBoxDelegate();
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QComboBox *combo;
};
class StyledComboBox : public QComboBox
{
Q_OBJECT
public:
StyledComboBox(QWidget *parent) : QComboBox(parent)
{
setItemDelegate(new ComboBoxDelegate(parent, this));
}
};
#endif // WIDGETS_H

View File

@ -1275,6 +1275,14 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
}
}
if (imgbuf.width() != vw || imgbuf.height() != vh)
{
imgbuf = QImage(vw, vh, QImage::Format_Indexed8);
imgbuf.setColor(0, qRgba(0, 0, 0, 0));
imgbuf.setColor(1, qRgba(255, 255, 255, 96));
imgbuf.setColor(2, qRgba(180, 64, 192, 255));
}
for (int sopt = D_DESERT; sopt < D_SPAWN; sopt++)
{
Level& l = lvs[sopt];
@ -1287,11 +1295,8 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
}
std::map<const QPixmap*, std::vector<QPainter::PixmapFragment>> frags;
QImage bufimg = QImage(vw, vh, QImage::Format_Indexed8);
bufimg.setColor(0, qRgba(0, 0, 0, 0));
bufimg.setColor(1, qRgba(255, 255, 255, 96));
bufimg.setColor(2, qRgba(180, 64, 192, 255));
bufimg.fill(0);
bool imgbuf_used = false;
imgbuf.fill(0);
for (Quad *q : l.cells)
{
@ -1384,8 +1389,11 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
if (q > r*r) continue;
int x = floor(d.x() + i);
int z = floor(d.y() + j);
if (bufimg.rect().contains(x, z))
bufimg.setPixel(x, z, q >= 2 ? 1 : 2);
if (imgbuf.rect().contains(x, z))
{
imgbuf.setPixel(x, z, q >= 2 ? 1 : 2);
imgbuf_used = true;
}
}
}
continue;
@ -1410,7 +1418,8 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
}
}
painter.drawImage(QPoint(0, 0), bufimg);
if (imgbuf_used)
painter.drawImage(QPoint(0, 0), imgbuf);
for (auto& it : frags)
painter.drawPixmapFragments(it.second.data(), it.second.size(), *it.first);
}

View File

@ -210,10 +210,13 @@ public:
// isdel is a flag for the worker thread to stop
std::atomic_bool isdel;
// slime overlay
// slime overlay (pixel chunks)
QImage slimeimg;
long slimex, slimez;
// overlay (visual pixels) for geodes
QImage imgbuf;
// shapes to overlay
std::vector<Shape> shapes;