Initial commit of Qt5 backend
Known issues: - Mouse and Key event translations are incomplete - Margin chaning is not implemented - uiBox controls can not be re-parented (see comment in qt5/tab.cpp) - It is bit ugly since margins have not been folded.. ..or any styling to make it look decent for that matter - Most signals are not supressed on programmatic changes TODO: - Finish unused drawmatrix function - Fix the uiBox so every uiControl is backed by a QWidget to simplify reparenting/margins
This commit is contained in:
parent
d88233a0fb
commit
e381e6b45f
|
@ -0,0 +1,74 @@
|
|||
# 28 may 2016
|
||||
|
||||
CXXFILES += \
|
||||
qt5/alloc.cpp \
|
||||
qt5/area.cpp \
|
||||
qt5/box.cpp \
|
||||
qt5/button.cpp \
|
||||
qt5/checkbox.cpp \
|
||||
|
||||
# qt5/child.cpp \
|
||||
|
||||
CXXFILES += \
|
||||
qt5/colorbutton.cpp \
|
||||
qt5/combobox.cpp \
|
||||
qt5/control.cpp \
|
||||
qt5/datetimepicker.cpp \
|
||||
qt5/debug.cpp \
|
||||
qt5/draw.cpp \
|
||||
qt5/drawmatrix.cpp \
|
||||
qt5/drawpath.cpp \
|
||||
qt5/drawtext.cpp \
|
||||
qt5/editablecombo.cpp \
|
||||
qt5/entry.cpp \
|
||||
qt5/fontbutton.cpp \
|
||||
qt5/group.cpp \
|
||||
qt5/label.cpp \
|
||||
qt5/main.cpp \
|
||||
qt5/menu.cpp \
|
||||
qt5/multilineentry.cpp \
|
||||
qt5/progressbar.cpp \
|
||||
qt5/radiobuttons.cpp \
|
||||
qt5/separator.cpp \
|
||||
qt5/slider.cpp \
|
||||
qt5/spinbox.cpp \
|
||||
qt5/stddialogs.cpp \
|
||||
qt5/tab.cpp \
|
||||
qt5/text.cpp \
|
||||
|
||||
# qt5/util.cpp \
|
||||
|
||||
CXXFILES += \
|
||||
qt5/window.cpp
|
||||
|
||||
HFILES += \
|
||||
qt5/draw.hpp \
|
||||
qt5/uipriv_qt5.hpp
|
||||
|
||||
# TODO split into a separate file or put in GNUmakefile.libui somehow?
|
||||
|
||||
# flags for Qt5
|
||||
CFLAGS += $(NATIVE_UI_CFLAGS)
|
||||
CXXFLAGS += $(NATIVE_UI_CXXFLAGS)
|
||||
LDFLAGS += $(NATIVE_UI_LDFLAGS)
|
||||
|
||||
|
||||
ifneq ($(RELEASE),1)
|
||||
CXXFLAGS += -DQT_DEBUG
|
||||
else
|
||||
# disable Q_ASSERT checks
|
||||
CXXFLAGS += -DQT_NO_DEBUG
|
||||
endif
|
||||
|
||||
# flags for building a shared library
|
||||
# OS X does support -shared but it has a preferred name for this so let's use that there instead; hence this is not gcc-global
|
||||
LDFLAGS += \
|
||||
-shared
|
||||
|
||||
# flags for warning on undefined symbols
|
||||
# this is not gcc-global because OS X doesn't support these flags
|
||||
# TODO figure out why FreeBSD follows linked libraries here
|
||||
ifneq ($(shell uname -s),FreeBSD)
|
||||
LDFLAGS += \
|
||||
-Wl,--no-undefined -Wl,--no-allow-shlib-undefined
|
||||
endif
|
|
@ -0,0 +1,8 @@
|
|||
ifndef PREFIX
|
||||
PREFIX=/usr
|
||||
endif
|
||||
|
||||
install: $(OUT)
|
||||
cp $(OUT) $(DESTDIR)$(PREFIX)/lib/libui.so.0
|
||||
ln -fs libui.so.0 $(DESTDIR)$(PREFIX)/lib/libui.so
|
||||
cp ui.h ui_$(OS).h $(DESTDIR)$(PREFIX)/include/
|
|
@ -0,0 +1,21 @@
|
|||
# 16 october 2015
|
||||
|
||||
EXESUFFIX =
|
||||
LIBSUFFIX = .so
|
||||
OSHSUFFIX = .h
|
||||
STATICLIBSUFFIX = .a
|
||||
TOOLCHAIN = gcc
|
||||
|
||||
# LONGTERM clean up all the NAMEs and SUFFIXs and NOSOSUFFIXs or whatever it was
|
||||
USESSONAME = 1
|
||||
SOVERSION = $(SOVERSION0)
|
||||
SONAMEEXT = $(LIBSUFFIX).$(SOVERSION)
|
||||
# this is not gcc-global because OS X uses a different filename format
|
||||
SONAMEFLAG = -Wl,-soname,
|
||||
|
||||
NATIVE_UI_CFLAGS += \
|
||||
`pkg-config --cflags Qt5Widgets`
|
||||
NATIVE_UI_CXXFLAGS += \
|
||||
`pkg-config --cflags Qt5Widgets`
|
||||
NATIVE_UI_LDFLAGS += \
|
||||
`pkg-config --libs Qt5Widgets` -lm
|
|
@ -0,0 +1,15 @@
|
|||
# 28 may 2016
|
||||
|
||||
$(OUT): $(OFILES) | $(OUTDIR)
|
||||
ifeq (,$(STATICLIB))
|
||||
@$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS)
|
||||
ifeq ($(USESSONAME),1)
|
||||
@ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME)
|
||||
endif
|
||||
else
|
||||
$(LD) -r $(OFILES) -o $(OUT:%.a=%.o)
|
||||
objcopy --localize-hidden $(OUT:%.a=%.o)
|
||||
$(AR) rcs $(OUT) $(OUT:%.a=%.o)
|
||||
endif
|
||||
@echo ====== Linked $(OUT)
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QHash>
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
static QHash<void*,const char *> allocations;
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
// not going to put a lock on this, just simple sanity check since the rest of the code doesn't do much about thread safety
|
||||
static QThread *uiThread = nullptr;
|
||||
#endif
|
||||
|
||||
void initAlloc(void)
|
||||
{
|
||||
#ifdef QT_DEBUG
|
||||
uiThread = QThread::currentThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void checkThread(const char *func)
|
||||
{
|
||||
#ifdef QT_DEBUG
|
||||
if (QThread::currentThread() != uiThread) {
|
||||
qWarning("Invoking %s on different thread from the one used for initialization, corruption will occur!", func);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
checkThread(QT_MESSAGELOG_FUNC);
|
||||
if (!allocations.isEmpty()) {
|
||||
qWarning("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:");
|
||||
for (auto it = allocations.constBegin(), e = allocations.constEnd(); it != e; ++it) {
|
||||
auto widget = uiValidateAndCastObjTo<QObject>((uiControl*)it.key());
|
||||
qWarning() << it.key() << ':' << it.value() << ':' << widget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
checkThread(QT_MESSAGELOG_FUNC);
|
||||
auto p = malloc(size);
|
||||
allocations[p] = type;
|
||||
return p;
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t new_size, const char *type)
|
||||
{
|
||||
checkThread(QT_MESSAGELOG_FUNC);
|
||||
|
||||
if (!allocations.remove(p)) {
|
||||
qFatal("Reallocing of invalid/non-existent allocation: %p type: %s", p, type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p = realloc(p, new_size);
|
||||
allocations[p] = type;
|
||||
return p;
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
checkThread(QT_MESSAGELOG_FUNC);
|
||||
if (!allocations.remove(p)) {
|
||||
qWarning("Ignoring freeing of invalid/non-existent allocation: %p", p);
|
||||
return;
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp"
|
||||
|
||||
#include <QScrollArea>
|
||||
#include <QPaintEvent>
|
||||
|
||||
#include <QDebug> // TODO: remove
|
||||
|
||||
struct uiArea : public uiQt5Control {};
|
||||
|
||||
class Area : public QWidget
|
||||
{
|
||||
Q_DISABLE_COPY(Area)
|
||||
|
||||
public:
|
||||
Area(uiAreaHandler *ah)
|
||||
: ah_(ah)
|
||||
{
|
||||
setAttribute(Qt::WA_Hover);
|
||||
}
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event)
|
||||
{
|
||||
uiDrawContext painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
const auto clip = event->rect();
|
||||
|
||||
// avoid using C99 initializer for portability *cough*MSVC*cough*
|
||||
uiAreaDrawParams params = {
|
||||
&painter, // Context
|
||||
(double)width(), // AreaWidth
|
||||
(double)height(), // AreaHeight
|
||||
(double)clip.x(), // ClipX;
|
||||
(double)clip.y(), // ClipY;
|
||||
(double)clip.width(), // ClipWidth;
|
||||
(double)clip.height(), // ClipHeight;
|
||||
};
|
||||
|
||||
ah_->Draw(ah_,static_cast<uiArea*>(uiFindQt5ControlForQObject(this)),¶ms);
|
||||
}
|
||||
|
||||
bool event(QEvent *event)
|
||||
{
|
||||
switch(event->type()) {
|
||||
//case QEvent::HoverMove: // don't care
|
||||
case QEvent::HoverEnter:
|
||||
case QEvent::HoverLeave:
|
||||
translateHoverEvent(static_cast<QHoverEvent*>(event));
|
||||
break;
|
||||
}
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
void translateHoverEvent(QHoverEvent *hoverEvent)
|
||||
{
|
||||
auto area = static_cast<uiArea*>(uiFindQt5ControlForQObject(this));
|
||||
switch(hoverEvent->type()) {
|
||||
case QEvent::HoverEnter:
|
||||
ah_->MouseCrossed(ah_,area,0);
|
||||
setFocus(Qt::MouseFocusReason); // so we get key events, hacky?
|
||||
setMouseTracking(true);
|
||||
break;
|
||||
case QEvent::HoverLeave:
|
||||
ah_->MouseCrossed(ah_,area,1);
|
||||
clearFocus();
|
||||
setMouseTracking(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent *mouseEvent) { translateMouseEvent(mouseEvent); }
|
||||
void mouseReleaseEvent(QMouseEvent *mouseEvent) { translateMouseEvent(mouseEvent); }
|
||||
void mouseDoubleClickEvent(QMouseEvent *mouseEvent) { translateMouseEvent(mouseEvent); }
|
||||
void mouseMoveEvent(QMouseEvent *mouseEvent) { translateMouseEvent(mouseEvent); }
|
||||
|
||||
void translateMouseEvent(QMouseEvent *mouseEvent)
|
||||
{
|
||||
static bool warned = false;
|
||||
if (warned) {
|
||||
qWarning("TODO: mouseEvent translation is incomplete!");
|
||||
warned = true;
|
||||
}
|
||||
uiAreaMouseEvent me;
|
||||
auto pos = mouseEvent->pos();
|
||||
|
||||
me.X = pos.x();
|
||||
me.Y = pos.y();
|
||||
|
||||
me.AreaWidth = width();
|
||||
me.AreaHeight = height();
|
||||
|
||||
// TODO: at a glance these are not tested nor well defined..?
|
||||
me.Down = 0; // TODO
|
||||
me.Up = 0; // TODO
|
||||
me.Count = 0; // TODO
|
||||
me.Modifiers = 0; // TODO
|
||||
me.Held1To64 = 0; // TODO
|
||||
|
||||
ah_->MouseEvent(ah_,static_cast<uiArea*>(uiFindQt5ControlForQObject(this)),&me);
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent *keyEvent) { translateKeyEvent(keyEvent); }
|
||||
void keyReleaseEvent(QKeyEvent *keyEvent) { translateKeyEvent(keyEvent); }
|
||||
|
||||
void translateKeyEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
|
||||
qWarning() << "TODO: keyEvent translation is incomplete!" << keyEvent;
|
||||
|
||||
ke.Key = keyEvent->key();
|
||||
ke.ExtKey = 0;
|
||||
ke.Modifier = 0;
|
||||
ke.Modifiers = 0;
|
||||
ke.Up = (keyEvent->type() == QEvent::KeyRelease);
|
||||
|
||||
ah_->KeyEvent(ah_,static_cast<uiArea*>(uiFindQt5ControlForQObject(this)),&ke);
|
||||
}
|
||||
|
||||
private:
|
||||
uiAreaHandler *ah_ = nullptr;
|
||||
};
|
||||
|
||||
static Area *findArea(uiArea *a)
|
||||
{
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(a)) {
|
||||
if (auto area = dynamic_cast<Area*>(widget)) {
|
||||
return area;
|
||||
}
|
||||
return widget->findChild<Area*>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height)
|
||||
{
|
||||
qWarning("TODO: %p %d, %d", (void *)a, (int)width, (int)height);
|
||||
}
|
||||
|
||||
void uiAreaQueueRedrawAll(uiArea *a)
|
||||
{
|
||||
if (auto area = findArea(a)) {
|
||||
area->update();;
|
||||
}
|
||||
}
|
||||
|
||||
void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
|
||||
{
|
||||
qWarning("TODO: %p %f, %f, %f, %f", (void *)a, x, y, width, height);
|
||||
}
|
||||
|
||||
uiArea *uiNewArea(uiAreaHandler *ah)
|
||||
{
|
||||
auto area = new Area(ah);
|
||||
|
||||
// The widget should get as much space as possible.
|
||||
area->setSizePolicy({QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding});
|
||||
|
||||
return uiAllocQt5ControlType(uiArea,area,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
|
||||
uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height)
|
||||
{
|
||||
auto area = new Area(ah);
|
||||
area->setMinimumSize({(int)width,(int)height});
|
||||
|
||||
auto scrollArea = new QScrollArea;
|
||||
scrollArea->setBackgroundRole(QPalette::Dark);
|
||||
scrollArea->setWidget(area);
|
||||
|
||||
return uiAllocQt5ControlType(uiArea,scrollArea,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <QLayoutItem>
|
||||
#include <QWidget>
|
||||
|
||||
struct uiBox : public uiQt5Control {};
|
||||
|
||||
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
|
||||
{
|
||||
if (auto layoutBox = uiValidateAndCastObjTo<QBoxLayout>(b)) {
|
||||
auto obj = uiValidateAndCastObjTo<QObject>(c);
|
||||
|
||||
if (auto layout = qobject_cast<QLayout*>(obj)) {
|
||||
layoutBox->addLayout(layout, stretchy);
|
||||
} else if (auto widget = qobject_cast<QWidget*>(obj)) {
|
||||
layoutBox->addWidget(widget);
|
||||
} else {
|
||||
qWarning("object is neither layout nor widget");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uiBoxDelete(uiBox *b, uintmax_t index)
|
||||
{
|
||||
if (auto layoutBox = uiValidateAndCastObjTo<QBoxLayout>(b)) {
|
||||
if (index < (uint)layoutBox->count()) {
|
||||
delete layoutBox->takeAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int uiBoxPadded(uiBox *b)
|
||||
{
|
||||
if (auto layoutBox = uiValidateAndCastObjTo<QBoxLayout>(b)) {
|
||||
layoutBox->spacing();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiBoxSetPadded(uiBox *b, int padded)
|
||||
{
|
||||
if (auto layoutBox = uiValidateAndCastObjTo<QBoxLayout>(b)) {
|
||||
layoutBox->setSpacing(padded);
|
||||
}
|
||||
}
|
||||
|
||||
uiBox *uiNewHorizontalBox(void)
|
||||
{
|
||||
return uiAllocQt5ControlType(uiBox, new QHBoxLayout, uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
|
||||
uiBox *uiNewVerticalBox(void)
|
||||
{
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
|
||||
return uiAllocQt5ControlType(uiBox, layout, uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
struct uiButton : public uiQt5Control {};
|
||||
|
||||
char *uiButtonText(uiButton *b)
|
||||
{
|
||||
if (auto button = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
return uiQt5StrdupQString(button->text());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiButtonSetText(uiButton *b, const char *text)
|
||||
{
|
||||
if (auto button = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
button->setText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data)
|
||||
{
|
||||
if (auto button = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
QObject::connect(button, &QPushButton::clicked, button, [f,b,data]{
|
||||
f(b,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiButton *uiNewButton(const char *text)
|
||||
{
|
||||
auto button = new QPushButton(QString::fromUtf8(text));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiButton,button,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
struct uiCheckbox : public uiQt5Control {};
|
||||
|
||||
char *uiCheckboxText(uiCheckbox *c)
|
||||
{
|
||||
if (auto checkBox = uiValidateAndCastObjTo<QCheckBox>(c)) {
|
||||
return uiQt5StrdupQString(checkBox->text());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiCheckboxSetText(uiCheckbox *c, const char *text)
|
||||
{
|
||||
if (auto checkBox = uiValidateAndCastObjTo<QCheckBox>(c)) {
|
||||
checkBox->setText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data)
|
||||
{
|
||||
if (auto checkBox = uiValidateAndCastObjTo<QCheckBox>(c)) {
|
||||
QObject::connect(checkBox, &QCheckBox::toggled, checkBox, [f,c,data]{
|
||||
f(c,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
int uiCheckboxChecked(uiCheckbox *c)
|
||||
{
|
||||
if (auto checkBox = uiValidateAndCastObjTo<QCheckBox>(c)) {
|
||||
return checkBox->isChecked();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiCheckboxSetChecked(uiCheckbox *c, int checked)
|
||||
{
|
||||
if (auto checkBox = uiValidateAndCastObjTo<QCheckBox>(c)) {
|
||||
checkBox->setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
uiCheckbox *uiNewCheckbox(const char *text)
|
||||
{
|
||||
auto checkBox = new QCheckBox(QString::fromUtf8(text));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiCheckbox,checkBox,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
struct uiColorButton : public uiQt5Control {};
|
||||
|
||||
static QColorDialog *findColorDialog(uiColorButton *b)
|
||||
{
|
||||
if (auto pushButton = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
if (auto colorDialog = pushButton->findChild<QColorDialog*>()) {
|
||||
return colorDialog;
|
||||
}
|
||||
}
|
||||
qWarning("colorDialog not found?!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a)
|
||||
{
|
||||
if (auto colorDialog = findColorDialog(b)) {
|
||||
auto color = colorDialog->currentColor();
|
||||
*r = color.redF();
|
||||
*g = color.greenF();
|
||||
*bl = color.blueF();
|
||||
*a = color.alphaF();
|
||||
}
|
||||
}
|
||||
|
||||
void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a)
|
||||
{
|
||||
if (auto colorDialog = findColorDialog(b)) {
|
||||
auto color = QColor::fromRgbF(r,g,bl,a);
|
||||
colorDialog->setCurrentColor(color);
|
||||
emit colorDialog->colorSelected(color); // hacky?
|
||||
}
|
||||
}
|
||||
|
||||
void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data)
|
||||
{
|
||||
if (auto colorDialog = findColorDialog(b)) {
|
||||
QObject::connect(colorDialog, &QColorDialog::colorSelected, colorDialog, [f,b,data]{
|
||||
f(b,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiColorButton *uiNewColorButton(void)
|
||||
{
|
||||
auto pushButton = new QPushButton;
|
||||
pushButton->setObjectName("colorButton"); // so we can target just this button with stylesheet
|
||||
|
||||
// persisting this dialog in the background simplies things, but not a good partice
|
||||
auto colorDialog = new QColorDialog(pushButton);
|
||||
|
||||
auto updateColor = [pushButton](const QColor &color) {
|
||||
// quick and dirty, probably not ideal, doesn't show transperency in anyway
|
||||
auto qssColorString = QStringLiteral("rgb(%1,%2,%3)").arg(color.red()).arg(color.green()).arg(color.blue());
|
||||
pushButton->setStyleSheet(QStringLiteral("#colorButton {background-color: %1;}").arg(qssColorString));
|
||||
};
|
||||
QObject::connect(colorDialog, &QColorDialog::colorSelected, pushButton, updateColor);
|
||||
updateColor({}); // set initial color (black)
|
||||
|
||||
QObject::connect(pushButton, &QPushButton::clicked, colorDialog, &QColorDialog::show);
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiColorButton,pushButton,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
struct uiCombobox : public uiQt5Control {};
|
||||
|
||||
void uiComboboxAppend(uiCombobox *c, const char *text)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
comboBox->addItem(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
intmax_t uiComboboxSelected(uiCombobox *c)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
return comboBox->currentIndex();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void uiComboboxSetSelected(uiCombobox *c, intmax_t n)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
comboBox->setCurrentIndex(n);
|
||||
}
|
||||
}
|
||||
|
||||
void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
// disambiguation of overloaded function
|
||||
void (QComboBox:: *currentIndexChanged)(int) = &QComboBox::currentIndexChanged;
|
||||
QObject::connect(comboBox, currentIndexChanged, comboBox, [f,c,data]{
|
||||
f(c,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiCombobox *uiNewCombobox(void)
|
||||
{
|
||||
auto comboBox = new QComboBox;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiCombobox,comboBox,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVariant>
|
||||
|
||||
#define uiQt5ControlSignature 0x51743578 // 'Qt5x';
|
||||
|
||||
Q_DECLARE_METATYPE(uiQt5Control *)
|
||||
|
||||
uiQt5Control *uiValidateQt5Control(uiControl *control)
|
||||
{
|
||||
if (control == nullptr) {
|
||||
qCritical("Called with uiControl == nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
if (control->Signature == uiQt5ControlSignature) {
|
||||
qCritical("Called with uiControl (%p) that does not contain correct uiQt5ControlSignature? "
|
||||
"Corruption or incorrect object passed in!", (void*)control);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto qt5Control = static_cast<uiQt5Control *>(control);;
|
||||
if (qt5Control->qobject == nullptr) {
|
||||
if ((qt5Control->flags & uiQt5Control::SuppressValidatationNag) == 0) {
|
||||
qCritical("Called with uiControl (%p) that doesn't have QObject (missing or already destroyed)", (void *)control);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qt5Control;
|
||||
}
|
||||
|
||||
uiQt5Control *uiFindQt5ControlForQObject(const QObject *qobject)
|
||||
{
|
||||
if (qobject) {
|
||||
return qobject->property("uiQt5Control").value<uiQt5Control *>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uiQt5Control *uiAllocQt5Control(uint32_t typesig, const char *typenamestr, QObject *qobject, uint32_t flags)
|
||||
{
|
||||
auto control = (uiQt5Control *)uiAllocControl(sizeof(uiQt5Control), uiQt5ControlSignature, typesig, typenamestr);
|
||||
control->qobject = qobject;
|
||||
control->flags = flags;
|
||||
qobject->setProperty("uiQt5Control", QVariant::fromValue(control));
|
||||
QObject::connect(qobject, &QWidget::destroyed, qobject, [control]{
|
||||
control->qobject = nullptr;
|
||||
if ((control->flags & uiQt5Control::DeleteControlOnQObjectFree) == 1) {
|
||||
// supress warning about the QObject being already deleted
|
||||
control->flags |= uiQt5Control::SuppressValidatationNag;
|
||||
uiFreeControl(control);
|
||||
}
|
||||
});
|
||||
|
||||
control->Destroy = [](uiControl *control) {
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
// consider synchronous destruction for simplicity? this is safer however depending on the caller state
|
||||
qt5Control->qobject->deleteLater();
|
||||
qt5Control->qobject->setProperty("uiQt5Control", {});
|
||||
|
||||
// disconnect the destroyed signal and explicity set widget to null since the control
|
||||
// will be gone when the actual widget destruction happens
|
||||
QObject::disconnect(qt5Control->qobject, &QWidget::destroyed, qt5Control->qobject, nullptr);
|
||||
qt5Control->qobject = nullptr;
|
||||
|
||||
uiFreeControl(control);
|
||||
}
|
||||
};
|
||||
control->Handle = [](uiControl *control) -> uintptr_t {
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
qWarning("VERIFY Handle usage");
|
||||
return (uintptr_t)qt5Control->qobject;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
control->Parent = [](uiControl *control) -> uiControl * {
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
qWarning("TODO Parent");
|
||||
Q_UNUSED(qt5Control);
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
control->SetParent = [](uiControl *control, uiControl *parent) {
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
Q_UNUSED(parent);
|
||||
qWarning("TODO SetParent");
|
||||
Q_UNUSED(qt5Control);
|
||||
}
|
||||
};
|
||||
control->Toplevel = [](uiControl *control) -> int {
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
qWarning("TODO Toplevel");
|
||||
Q_UNUSED(qt5Control);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
control->Visible = [](uiControl *control) -> int {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->isVisible();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
control->Show = [](uiControl *control) {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->show();
|
||||
}
|
||||
};
|
||||
control->Hide = [](uiControl *control) {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->hide();
|
||||
}
|
||||
};
|
||||
control->Enabled = [](uiControl *control) -> int {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->isEnabled();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
control->Enable = [](uiControl *control) {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->setEnabled(true);
|
||||
}
|
||||
};
|
||||
control->Disable = [](uiControl *control) {
|
||||
if (auto widget = uiValidateAndCastObjTo<QWidget>(control)) {
|
||||
return widget->setEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
return control;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
//#include <QCalendarWidget> // TODO: for dropdown
|
||||
|
||||
#include <QDateEdit>
|
||||
#include <QTimeEdit>
|
||||
#include <QDateTimeEdit>
|
||||
|
||||
struct uiDateTimePicker : public uiQt5Control {};
|
||||
|
||||
uiDateTimePicker *uiNewDateTimePicker(void)
|
||||
{
|
||||
auto dateEdit = new QDateEdit(QDate::currentDate());
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiDateTimePicker,dateEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDatePicker(void)
|
||||
{
|
||||
auto timeEdit = new QTimeEdit(QTime::currentTime());
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiDateTimePicker,timeEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewTimePicker(void)
|
||||
{
|
||||
auto dateTimeEdit = new QDateTimeEdit(QDateTime::currentDateTime());
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiDateTimePicker,dateTimeEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
qFatal("[libui] %s:%s:%s() %s%s", file, line, func, prefix, qPrintable(QString::vasprintf(format,ap)));
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp"
|
||||
|
||||
static QGradient uiDrawBrushToQGradient(const uiDrawBrush *b, QGradient gradient)
|
||||
{
|
||||
for (size_t i=0; i<b->NumStops; ++i) {
|
||||
const auto &stop = b->Stops[i];
|
||||
const auto color = QColor::fromRgbF(stop.R, stop.G, stop.B, stop.A);
|
||||
gradient.setColorAt(stop.Pos, color);
|
||||
}
|
||||
return gradient;
|
||||
}
|
||||
|
||||
static QBrush uiDrawBrushToQBrush(const uiDrawBrush *b)
|
||||
{
|
||||
switch (b->Type)
|
||||
{
|
||||
case uiDrawBrushTypeSolid:
|
||||
return {QColor::fromRgbF(b->R, b->G, b->B, b->A), Qt::SolidPattern};
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
return uiDrawBrushToQGradient(b, QLinearGradient(b->X0, b->Y0, b->X1, b->Y1));
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
return uiDrawBrushToQGradient(b, QRadialGradient(b->X0, b->Y0, b->OuterRadius, b->X1, b->Y1, 0));
|
||||
case uiDrawBrushTypeImage:
|
||||
// not implemented in uiDrawBrush at time of writing
|
||||
qWarning("TODO: uiDrawBrushTypeImage");
|
||||
break;
|
||||
default:
|
||||
qWarning("Unknown uiDrawBrushType: %d", b->Type);
|
||||
}
|
||||
// something noticable
|
||||
return {Qt::magenta, Qt::DiagCrossPattern};
|
||||
}
|
||||
|
||||
static QPen uiDrawStrokeParamsToQPen(const uiDrawStrokeParams *p, const QBrush &brush)
|
||||
{
|
||||
QPen pen(brush, p->Thickness);
|
||||
|
||||
switch (p->Cap)
|
||||
{
|
||||
case uiDrawLineCapFlat:
|
||||
pen.setCapStyle(Qt::FlatCap);
|
||||
break;
|
||||
case uiDrawLineCapRound:
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
break;
|
||||
case uiDrawLineCapSquare:
|
||||
pen.setCapStyle(Qt::SquareCap);
|
||||
break;
|
||||
default:
|
||||
qWarning("Unknown uiDrawLineCap: %d", p->Cap);
|
||||
}
|
||||
|
||||
switch (p->Join)
|
||||
{
|
||||
case uiDrawLineJoinMiter:
|
||||
pen.setJoinStyle(Qt::MiterJoin);
|
||||
break;
|
||||
case uiDrawLineJoinRound:
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
break;
|
||||
case uiDrawLineJoinBevel:
|
||||
pen.setJoinStyle(Qt::BevelJoin);
|
||||
break;
|
||||
default:
|
||||
qWarning("Unknown uiDrawLineJoin: %d", p->Join);
|
||||
}
|
||||
|
||||
pen.setMiterLimit(p->MiterLimit);
|
||||
|
||||
if (p->NumDashes) {
|
||||
pen.setDashOffset(p->DashPhase/10.0);
|
||||
QVector<qreal> pattern;
|
||||
for (size_t i=0; i<p->NumDashes; ++i) {
|
||||
pattern.append(p->Dashes[i]/10.0);
|
||||
}
|
||||
pen.setDashPattern(pattern);
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
|
||||
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
|
||||
{
|
||||
c->strokePath(*path, uiDrawStrokeParamsToQPen(p, uiDrawBrushToQBrush(b)));
|
||||
}
|
||||
|
||||
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||
{
|
||||
c->fillPath(*path, uiDrawBrushToQBrush(b));
|
||||
}
|
||||
|
||||
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
||||
{
|
||||
c->setTransform(m2t(m), true /* combine */);
|
||||
}
|
||||
|
||||
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||
{
|
||||
c->setClipPath(*path);
|
||||
}
|
||||
|
||||
void uiDrawSave(uiDrawContext *c)
|
||||
{
|
||||
c->save();
|
||||
}
|
||||
|
||||
void uiDrawRestore(uiDrawContext *c)
|
||||
{
|
||||
c->restore();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
typedef struct uiDrawContext uiDrawContext;
|
||||
|
||||
struct uiDrawContext : public QPainter
|
||||
{
|
||||
uiDrawContext(QPaintDevice *device)
|
||||
: QPainter(device)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#include <QPainterPath>
|
||||
|
||||
struct uiDrawPath : QPainterPath
|
||||
{
|
||||
uiDrawPath(uiDrawFillMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case uiDrawFillModeWinding:
|
||||
setFillRule(Qt::WindingFill);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
default:
|
||||
qWarning("Unknown uiDrawFillMode: %d", mode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern QTransform m2t(const uiDrawMatrix *m);
|
||||
|
||||
struct uiDrawTextFont : public QFont
|
||||
{
|
||||
uiDrawTextFont(const QFont &font)
|
||||
: QFont(font) {}
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp"
|
||||
|
||||
QTransform m2t(const uiDrawMatrix *m)
|
||||
{
|
||||
return QTransform(m->M11, m->M12, m->M21, m->M22, m->M31, m->M32);
|
||||
}
|
||||
|
||||
static void t2m(const QTransform &t, uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = t.m11();
|
||||
m->M12 = t.m12();
|
||||
m->M21 = t.m21();
|
||||
m->M22 = t.m22();
|
||||
m->M31 = t.m31();
|
||||
m->M32 = t.m32();
|
||||
}
|
||||
|
||||
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
||||
{
|
||||
auto t = m2t(m);
|
||||
t.translate(x,y);
|
||||
t2m(t,m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
||||
{
|
||||
auto t = m2t(m);
|
||||
t.translate(x,y);
|
||||
t.scale(x,y);
|
||||
t.translate(-x,-y);
|
||||
t2m(t,m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
||||
{
|
||||
auto t = m2t(m);
|
||||
t.translate(x,y);
|
||||
t.rotateRadians(amount);
|
||||
t.translate(-x,-y);
|
||||
t2m(t,m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
auto t = m2t(m);
|
||||
t.translate(x,y);
|
||||
t.shear(xamount, yamount);
|
||||
t.translate(-x,-y);
|
||||
t2m(t,m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
||||
{
|
||||
qWarning("TODO: %p, %p", (void *)dest, (void *)src);
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
||||
{
|
||||
qWarning("TODO: %p", (void *)m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
||||
{
|
||||
qWarning("TODO: %p", (void *)m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
qWarning("TODO: %p, %f %f", (void *)m, *x, *y);
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
qWarning("TODO: %p, %f %f", (void *)m, *x, *y);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
||||
{
|
||||
return new uiDrawPath(mode);
|
||||
}
|
||||
|
||||
void uiDrawFreePath(uiDrawPath *p)
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
p->moveTo(x,y);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
qreal xStart = xCenter + radius*qCos(startAngle);
|
||||
qreal yStart = yCenter + radius*qSin(startAngle);
|
||||
p->moveTo(xStart, yStart);
|
||||
if (negative) {
|
||||
sweep = -(2*M_PI-sweep);
|
||||
}
|
||||
p->arcTo(xCenter-radius, yCenter-radius, radius*2, radius*2, -qRadiansToDegrees(startAngle), -qRadiansToDegrees(sweep));
|
||||
}
|
||||
|
||||
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
p->lineTo(x,y);
|
||||
}
|
||||
|
||||
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
if (negative) {
|
||||
sweep = -(2*M_PI-sweep);
|
||||
}
|
||||
p->arcTo(xCenter-radius, yCenter-radius, radius*2, radius*2, -qRadiansToDegrees(startAngle), -qRadiansToDegrees(sweep));
|
||||
}
|
||||
|
||||
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
||||
{
|
||||
p->cubicTo(c1x, c1y, c2x, c2y, endX, endY);
|
||||
}
|
||||
|
||||
void uiDrawPathCloseFigure(uiDrawPath *p)
|
||||
{
|
||||
p->closeSubpath();
|
||||
}
|
||||
|
||||
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
||||
{
|
||||
p->addRect(x,y,width,height);
|
||||
}
|
||||
|
||||
void uiDrawPathEnd(uiDrawPath *p)
|
||||
{
|
||||
Q_UNUSED(p);
|
||||
}
|
||||
|
||||
uiDrawFillMode pathFillMode(uiDrawPath *path)
|
||||
{
|
||||
switch (path->fillRule())
|
||||
{
|
||||
case Qt::WindingFill: return uiDrawFillModeWinding;
|
||||
case Qt::OddEvenFill: return uiDrawFillModeAlternate;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp" // draw context
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QFontMetricsF>
|
||||
#include <QTextLayout>
|
||||
|
||||
struct uiDrawFontFamilies : public QStringList
|
||||
{
|
||||
uiDrawFontFamilies(const QStringList &stringList)
|
||||
: QStringList(stringList) {}
|
||||
};
|
||||
|
||||
struct uiDrawTextLayout : public QTextLayout
|
||||
{
|
||||
uiDrawTextLayout(const QString& text, const QFont &font, double width)
|
||||
: QTextLayout(text, font)
|
||||
, width_(width)
|
||||
{
|
||||
// qWarning("TODO: figure out wtf width is for: %f", width_);
|
||||
}
|
||||
double width_ = 0.0;
|
||||
};
|
||||
|
||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||
{
|
||||
return new uiDrawFontFamilies(QFontDatabase().families());
|
||||
}
|
||||
|
||||
uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
return ff->count();
|
||||
}
|
||||
|
||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n)
|
||||
{
|
||||
return uiQt5StrdupQString(ff->value(n));
|
||||
}
|
||||
|
||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
delete ff;
|
||||
}
|
||||
|
||||
static int uiDrawTextWeightToFontWeight(uiDrawTextWeight weight)
|
||||
{
|
||||
switch (weight)
|
||||
{
|
||||
case uiDrawTextWeightThin: return QFont::Thin;
|
||||
case uiDrawTextWeightUltraLight: return QFont::ExtraLight;
|
||||
case uiDrawTextWeightLight: return QFont::Light;
|
||||
case uiDrawTextWeightBook: return (QFont::Light+QFont::Normal)/2;
|
||||
case uiDrawTextWeightNormal: return QFont::Normal;
|
||||
case uiDrawTextWeightMedium: return QFont::Medium;
|
||||
case uiDrawTextWeightSemiBold: return QFont::DemiBold;
|
||||
case uiDrawTextWeightBold: return QFont::Bold;
|
||||
case uiDrawTextWeightUtraBold: return QFont::ExtraBold;
|
||||
case uiDrawTextWeightHeavy: return (QFont::ExtraBold+QFont::Black)/2;
|
||||
case uiDrawTextWeightUltraHeavy: return QFont::Black;
|
||||
default:
|
||||
qWarning("Unknown uiDrawTextWeight: %d", weight);
|
||||
}
|
||||
return QFont::Normal;
|
||||
}
|
||||
|
||||
static int uiDrawTextItalicToFontStyle(uiDrawTextItalic italic)
|
||||
{
|
||||
switch(italic)
|
||||
{
|
||||
case uiDrawTextItalicNormal: return QFont::StyleNormal;
|
||||
case uiDrawTextItalicOblique: return QFont::StyleOblique;
|
||||
case uiDrawTextItalicItalic: return QFont::StyleItalic;
|
||||
default:
|
||||
qWarning("Unknown uiDrawTextItalic: %d", italic);
|
||||
}
|
||||
return QFont::StyleNormal;
|
||||
}
|
||||
|
||||
static int uiDrawTextStretchToFont(uiDrawTextStretch stretch)
|
||||
{
|
||||
switch(stretch)
|
||||
{
|
||||
case uiDrawTextStretchUltraCondensed: return QFont::UltraCondensed;
|
||||
case uiDrawTextStretchExtraCondensed: return QFont::ExtraCondensed;
|
||||
case uiDrawTextStretchCondensed: return QFont::Condensed;
|
||||
case uiDrawTextStretchSemiCondensed: return QFont::SemiCondensed;
|
||||
case uiDrawTextStretchNormal: return QFont::Unstretched;
|
||||
case uiDrawTextStretchSemiExpanded: return QFont::SemiExpanded;
|
||||
case uiDrawTextStretchExpanded: return QFont::Expanded;
|
||||
case uiDrawTextStretchExtraExpanded: return QFont::ExtraExpanded;
|
||||
case uiDrawTextStretchUltraExpanded: return QFont::UltraExpanded;
|
||||
default:
|
||||
qWarning("Unknown uiDrawTextStretch: %d", stretch);
|
||||
}
|
||||
return QFont::Unstretched;
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
QFont font(QString::fromUtf8(desc->Family));
|
||||
font.setPointSizeF(desc->Size);
|
||||
font.setWeight(uiDrawTextWeightToFontWeight(desc->Weight));
|
||||
font.setItalic(uiDrawTextItalicToFontStyle(desc->Italic));
|
||||
font.setStretch(uiDrawTextStretchToFont(desc->Stretch));
|
||||
|
||||
return new uiDrawTextFont(font);
|
||||
}
|
||||
|
||||
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
||||
{
|
||||
delete font;
|
||||
}
|
||||
|
||||
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
||||
{
|
||||
qWarning("TODO: %p", (void *)font);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
qWarning("TODO: %p %p", (void *)font, (void *)desc);
|
||||
}
|
||||
|
||||
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
||||
{
|
||||
if (font) {
|
||||
QFontMetricsF fontMetrics(*font);
|
||||
metrics->Ascent = fontMetrics.ascent();
|
||||
metrics->Descent = fontMetrics.descent();
|
||||
metrics->Leading = fontMetrics.leading();
|
||||
metrics->UnderlinePos = fontMetrics.underlinePos();
|
||||
metrics->UnderlineThickness = 0; // TODO?
|
||||
}
|
||||
}
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
|
||||
{
|
||||
return new uiDrawTextLayout(QString::fromUtf8(text), *defaultFont, width);
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
||||
{
|
||||
delete layout;
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
||||
{
|
||||
layout->width_ = width;
|
||||
}
|
||||
|
||||
static void runLayouting(uiDrawTextLayout *layout)
|
||||
{
|
||||
layout->setCacheEnabled(true);
|
||||
layout->beginLayout();
|
||||
forever {
|
||||
QTextLine line = layout->createLine();
|
||||
if (!line.isValid()) {
|
||||
break;
|
||||
}
|
||||
// TODO: shift by line height?
|
||||
}
|
||||
layout->endLayout();
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
||||
{
|
||||
runLayouting(layout);
|
||||
auto boundingRect = layout->boundingRect();
|
||||
*width = boundingRect.width();
|
||||
*height = boundingRect.height();
|
||||
}
|
||||
|
||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
runLayouting(layout);
|
||||
layout->draw(c, {x,y});
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a)
|
||||
{
|
||||
// ineffective..? but works
|
||||
auto additionalFormats = layout->additionalFormats();
|
||||
QTextCharFormat format;
|
||||
format.setForeground(QColor::fromRgbF(r,g,b,a));
|
||||
additionalFormats.append(QTextLayout::FormatRange{(int)startChar,(int)(1+endChar-startChar),format});
|
||||
layout->setAdditionalFormats(additionalFormats);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
struct uiEditableCombobox : public uiQt5Control {};
|
||||
|
||||
|
||||
void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
comboBox->addItem(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
intmax_t uiEditableComboboxSelected(uiEditableCombobox *c)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
return comboBox->currentIndex();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void uiEditableComboboxSetSelected(uiEditableCombobox *c, intmax_t n)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
return comboBox->setCurrentIndex(n);
|
||||
}
|
||||
}
|
||||
|
||||
char *uiEditableComboboxText(uiEditableCombobox *c)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
return uiQt5StrdupQString(comboBox->currentText());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
comboBox->setCurrentText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data)
|
||||
{
|
||||
if (auto comboBox = uiValidateAndCastObjTo<QComboBox>(c)) {
|
||||
// disambiguation of overloaded function
|
||||
void (QComboBox:: *currentIndexChanged)(const QString &) = &QComboBox::currentIndexChanged;
|
||||
QObject::connect(comboBox, currentIndexChanged, comboBox, [f,c,data]{
|
||||
f(c,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiEditableCombobox *uiNewEditableCombobox(void)
|
||||
{
|
||||
auto comboBox = new QComboBox;
|
||||
comboBox->setEditable(true);
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiEditableCombobox,comboBox,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
struct uiEntry : uiQt5Control {};
|
||||
|
||||
char *uiEntryText(uiEntry *e)
|
||||
{
|
||||
if (auto lineEdit = uiValidateAndCastObjTo<QLineEdit>(e)) {
|
||||
return uiQt5StrdupQString(lineEdit->text());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiEntrySetText(uiEntry *e, const char *text)
|
||||
{
|
||||
if (auto lineEdit = uiValidateAndCastObjTo<QLineEdit>(e)) {
|
||||
lineEdit->setText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
|
||||
{
|
||||
if (auto lineEdit = uiValidateAndCastObjTo<QLineEdit>(e)) {
|
||||
// Unlike textChanged(), this signal is not emitted when the text is changed programmatically
|
||||
QObject::connect(lineEdit, &QLineEdit::textChanged, lineEdit, [f,e,data]{
|
||||
f(e,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
int uiEntryReadOnly(uiEntry *e)
|
||||
{
|
||||
if (auto lineEdit = uiValidateAndCastObjTo<QLineEdit>(e)) {
|
||||
return lineEdit->isReadOnly();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void uiEntrySetReadOnly(uiEntry *e, int readonly)
|
||||
{
|
||||
if (auto lineEdit = uiValidateAndCastObjTo<QLineEdit>(e)) {
|
||||
lineEdit->setReadOnly(readonly);
|
||||
}
|
||||
}
|
||||
|
||||
uiEntry *uiNewEntry(void)
|
||||
{
|
||||
auto lineEdit = new QLineEdit;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiEntry,lineEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include "draw.hpp"
|
||||
|
||||
// another way would be to return a QFontComboBox + QSpinBox (for font size),
|
||||
// but this is more true the control name
|
||||
#include <QFontDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
struct uiFontButton : public uiQt5Control {};
|
||||
|
||||
uiDrawTextFont *uiFontButtonFont(uiFontButton *b)
|
||||
{
|
||||
if (auto pushButton = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
if (auto fontDialog = pushButton->findChild<QFontDialog*>()) {
|
||||
return new uiDrawTextFont(fontDialog->currentFont());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data)
|
||||
{
|
||||
if (auto pushButton = uiValidateAndCastObjTo<QPushButton>(b)) {
|
||||
if (auto fontDialog = pushButton->findChild<QFontDialog*>()) {
|
||||
QObject::connect(fontDialog, &QFontDialog::fontSelected, fontDialog, [f,b,data]{
|
||||
f(b,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiFontButton *uiNewFontButton(void)
|
||||
{
|
||||
auto pushButton = new QPushButton;
|
||||
|
||||
// persisting this dialog in the background simplies things, but not a good partice
|
||||
auto fontDialog = new QFontDialog(pushButton);
|
||||
|
||||
auto updateFont = [pushButton](const QFont &font) {
|
||||
auto family = font.family();
|
||||
auto pointSize = font.pointSize(); // or pixelSize..?
|
||||
pushButton->setText(QStringLiteral("%1 %2").arg(family).arg(pointSize));
|
||||
};
|
||||
QObject::connect(fontDialog, &QFontDialog::fontSelected, pushButton, updateFont);
|
||||
QObject::connect(pushButton, &QPushButton::clicked, fontDialog, &QFontDialog::show);
|
||||
|
||||
QFont font; font.setPointSize(12);
|
||||
updateFont(font);
|
||||
fontDialog->setCurrentFont(font); // set initial font (system font)
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiFontButton,pushButton,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
struct uiGroup : public uiQt5Control {};
|
||||
|
||||
char *uiGroupTitle(uiGroup *g)
|
||||
{
|
||||
if (auto groupBox = uiValidateAndCastObjTo<QGroupBox>(g)) {
|
||||
return uiQt5StrdupQString(groupBox->title());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiGroupSetTitle(uiGroup *g, const char *text)
|
||||
{
|
||||
if (auto groupBox = uiValidateAndCastObjTo<QGroupBox>(g)) {
|
||||
groupBox->setTitle(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiGroupSetChild(uiGroup *g, uiControl *child)
|
||||
{
|
||||
if (auto groupBox = uiValidateAndCastObjTo<QGroupBox>(g)) {
|
||||
auto obj = uiValidateAndCastObjTo<QObject>(child);
|
||||
if (groupBox->layout()) {
|
||||
groupBox->layout()->deleteLater();
|
||||
}
|
||||
if (auto layout = qobject_cast<QLayout*>(obj)) {
|
||||
groupBox->setLayout(layout);
|
||||
} else if (auto widget = qobject_cast<QWidget*>(obj)) {
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->setMargin(0); // ?
|
||||
layout->addWidget(widget);
|
||||
groupBox->setLayout(layout);
|
||||
} else {
|
||||
qWarning("object is neither layout nor widget");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int uiGroupMargined(uiGroup *g)
|
||||
{
|
||||
qWarning("TODO: %p", (void*)g);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiGroupSetMargined(uiGroup *g, int margined)
|
||||
{
|
||||
qWarning("TODO: %p, %d", (void*)g, margined);
|
||||
}
|
||||
|
||||
uiGroup *uiNewGroup(const char *text)
|
||||
{
|
||||
auto groupBox = new QGroupBox(QString::fromUtf8(text));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiGroup,groupBox,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
struct uiLabel : public uiQt5Control {};
|
||||
|
||||
char *uiLabelText(uiLabel *l)
|
||||
{
|
||||
if (auto label = uiValidateAndCastObjTo<QLabel>(l)) {
|
||||
return uiQt5StrdupQString(label->text());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiLabelSetText(uiLabel *l, const char *text)
|
||||
{
|
||||
if (auto label = uiValidateAndCastObjTo<QLabel>(l)) {
|
||||
label->setText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
uiLabel *uiNewLabel(const char *text)
|
||||
{
|
||||
auto label = new QLabel(QString::fromUtf8(text));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiLabel,label,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
#include <QThread>
|
||||
|
||||
#include <functional>
|
||||
|
||||
const char *styleSheet = R"qcss(
|
||||
QGroupBox {
|
||||
font: bold;
|
||||
color: #333;
|
||||
/*border: none;*/
|
||||
}
|
||||
)qcss";
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
Q_UNUSED(o); // don't care about something that isn't used
|
||||
|
||||
if (QCoreApplication::instance() != nullptr) {
|
||||
return "another QApplication instance already exists";
|
||||
}
|
||||
|
||||
static int argc = 0;
|
||||
static const char *argv[] = {"libui"};
|
||||
auto app = new QApplication(argc,(char **)argv);
|
||||
|
||||
if (app == nullptr) {
|
||||
return "failed to allocate new QApplication";
|
||||
}
|
||||
|
||||
qSetMessagePattern("%{file}:%{line} %{function}(): %{message}");
|
||||
|
||||
// few consistency things
|
||||
app->setQuitOnLastWindowClosed(false);
|
||||
app->setStyleSheet(styleSheet);
|
||||
|
||||
initAlloc();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
auto app = static_cast<QApplication*>(QCoreApplication::instance());
|
||||
|
||||
// not strictly necessary, just so we don't get needless nag from uninitAlloc()
|
||||
// becasue of cascading it's not possible to iterate over the list (copy)
|
||||
while (app->topLevelWidgets().count()) {
|
||||
delete app->topLevelWidgets().first();
|
||||
}
|
||||
|
||||
uninitAlloc();
|
||||
|
||||
delete app;
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
Q_UNUSED(err); // we only return static constants for errors
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
if (QApplication::instance()) {
|
||||
QApplication::instance()->exec();
|
||||
}
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
if (QApplication::instance()) {
|
||||
if (wait) {
|
||||
// processEvents will always return immediately if there are no
|
||||
// immediate events to process
|
||||
|
||||
// we would have to install a event listener and run in while loop
|
||||
// that seems very questionable.. don't understand the use case
|
||||
qWarning("TODO: wait - no equivalent?");
|
||||
}
|
||||
QApplication::instance()->processEvents();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
QTimer::singleShot(0,QApplication::instance(), &QApplication::quit);
|
||||
}
|
||||
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
if (!QCoreApplication::instance()) {
|
||||
// no instance...?
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread()->eventDispatcher()) {
|
||||
// the simple way to queue something to run on main thread
|
||||
QTimer::singleShot(0,QCoreApplication::instance(), [f,data]{
|
||||
f(data);
|
||||
});
|
||||
} else {
|
||||
// very dirty workaround, spawn a new thread.. with event dispatcher to deliver this
|
||||
|
||||
// this could be done with custom signal, but since I decided to do
|
||||
// this without using moc this is the simplest solution I could think of.
|
||||
|
||||
// sort of works, but not very light weight and will block to ensure resource cleanup
|
||||
class Thread : public QThread
|
||||
{
|
||||
std::function<void ()> taskForMain_;
|
||||
|
||||
void run()
|
||||
{
|
||||
QTimer::singleShot(0,QCoreApplication::instance(), taskForMain_);
|
||||
exec(); // start event loop
|
||||
}
|
||||
public:
|
||||
Thread(std::function<void ()> taskForMain)
|
||||
: taskForMain_(taskForMain)
|
||||
{
|
||||
start();
|
||||
}
|
||||
~Thread()
|
||||
{
|
||||
quit();
|
||||
wait();
|
||||
}
|
||||
};
|
||||
Thread([f,data]{
|
||||
f(data);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMenuBar>
|
||||
|
||||
struct uiMenu : public uiQt5Control {};
|
||||
|
||||
#ifndef uiMenuSignature
|
||||
#define uiMenuSignature 0x4d656e75 // 'Menu'
|
||||
#endif
|
||||
|
||||
struct uiMenuItem : public uiQt5Control {};
|
||||
|
||||
#ifndef uiMenuItemSignature
|
||||
#define uiMenuItemSignature 0x4d654974 // 'MeIt'
|
||||
#endif
|
||||
|
||||
void uiMenuItemEnable(uiMenuItem *item)
|
||||
{
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(item)) {
|
||||
action->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void uiMenuItemDisable(uiMenuItem *item)
|
||||
{
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(item)) {
|
||||
action->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
static uiWindow *uiQt5FindWindow(QObject *qobject)
|
||||
{
|
||||
if (qobject) {
|
||||
if (auto widget = qobject_cast<QWidget*>(qobject)) {
|
||||
if (auto topLevel = widget->topLevelWidget()) {
|
||||
return static_cast<uiWindow *>(uiFindQt5ControlForQObject(topLevel));
|
||||
}
|
||||
}
|
||||
return uiQt5FindWindow(qobject->parent());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static uiWindow *uiQt5FindWindow(uiControl *leaf)
|
||||
{
|
||||
if (auto qobject = uiValidateAndCastObjTo<QObject>(leaf)) {
|
||||
return uiQt5FindWindow(qobject);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data)
|
||||
{
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(item)) {
|
||||
QObject::connect(action,&QAction::triggered, action, [f,item,data]{
|
||||
auto window = uiQt5FindWindow(item);
|
||||
f(item,window,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
int uiMenuItemChecked(uiMenuItem *item)
|
||||
{
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(item)) {
|
||||
return action->isChecked();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiMenuItemSetChecked(uiMenuItem *item, int checked)
|
||||
{
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(item)) {
|
||||
action->setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)
|
||||
{
|
||||
if (auto menu = uiValidateAndCastObjTo<QMenu>(m)) {
|
||||
auto action = menu->addAction(name);
|
||||
|
||||
action->setObjectName(name); // for debugging only
|
||||
|
||||
return uiAllocQt5ControlType(uiMenuItem, action, uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name)
|
||||
{
|
||||
auto menuItem = uiMenuAppendItem(m, name);
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(menuItem)) {
|
||||
action->setCheckable(true);
|
||||
}
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendQuitItem(uiMenu *m)
|
||||
{
|
||||
uiMenuAppendSeparator(m);
|
||||
auto menuItem = uiMenuAppendItem(m, "&Quit");
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(menuItem)) {
|
||||
action->setShortcut(QKeySequence::Quit);
|
||||
}
|
||||
uiMenuItemOnClicked(menuItem, [](uiMenuItem *, uiWindow *, void *) { uiQuit(); }, nullptr);
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m)
|
||||
{
|
||||
uiMenuAppendSeparator(m);
|
||||
auto menuItem = uiMenuAppendItem(m, "&Preferences...");
|
||||
if (auto action = uiValidateAndCastObjTo<QAction>(menuItem)) {
|
||||
action->setShortcut(QKeySequence::Preferences);
|
||||
}
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendAboutItem(uiMenu *m)
|
||||
{
|
||||
uiMenuAppendSeparator(m);
|
||||
return uiMenuAppendItem(m, "&About");
|
||||
}
|
||||
|
||||
void uiMenuAppendSeparator(uiMenu *m)
|
||||
{
|
||||
if (auto menu = uiValidateAndCastObjTo<QMenu>(m)) {
|
||||
menu->addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
QMenuBar *uiQt5FindMainMenuBar()
|
||||
{
|
||||
auto app = static_cast<QApplication*>(QCoreApplication::instance());
|
||||
for (auto widget : app->topLevelWidgets()) {
|
||||
// see if the widget is the menubar
|
||||
if (auto menuBar = qobject_cast<QMenuBar*>(widget)) {
|
||||
return menuBar;
|
||||
}
|
||||
// see if the widget owns the menubar
|
||||
if (auto menuBar = widget->findChild<QMenuBar*>()) {
|
||||
return menuBar;
|
||||
}
|
||||
}
|
||||
// not found, create
|
||||
return new QMenuBar;
|
||||
}
|
||||
|
||||
uiMenu *uiNewMenu(const char *name)
|
||||
{
|
||||
auto menuBar = uiQt5FindMainMenuBar();
|
||||
auto menu = new QMenu(QString::fromUtf8(name), menuBar);
|
||||
menuBar->addMenu(menu);
|
||||
|
||||
menu->setObjectName(name); // for debugging only
|
||||
|
||||
return uiAllocQt5ControlType(uiMenu, menu, uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QTextEdit>
|
||||
|
||||
struct uiMultilineEntry : public uiQt5Control {};
|
||||
|
||||
char *uiMultilineEntryText(uiMultilineEntry *e)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
return uiQt5StrdupQString(textEdit->toPlainText());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
textEdit->setPlainText(QString::fromUtf8(text));
|
||||
}
|
||||
}
|
||||
|
||||
void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
// append is least interferring (keeps selection and cursor),
|
||||
// but will add newline by it self hence not exactly compatible
|
||||
|
||||
// since the newline is implicitly inserted we need to remove
|
||||
// the explicitly inserted one if we want the examples to look
|
||||
// same, but this may cause other issues..
|
||||
auto oldBlockSignals = textEdit->blockSignals(true);
|
||||
textEdit->append(QString::fromUtf8(text).trimmed());
|
||||
textEdit->blockSignals(oldBlockSignals);
|
||||
}
|
||||
}
|
||||
|
||||
void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
QObject::connect(textEdit, &QTextEdit::textChanged, textEdit, [f,e,data]{
|
||||
f(e,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
int uiMultilineEntryReadOnly(uiMultilineEntry *e)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
textEdit->isReadOnly();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)
|
||||
{
|
||||
if (auto textEdit = uiValidateAndCastObjTo<QTextEdit>(e)) {
|
||||
textEdit->setReadOnly(readonly);
|
||||
}
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewMultilineEntry(void)
|
||||
{
|
||||
auto textEdit = new QTextEdit;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiMultilineEntry,textEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)
|
||||
{
|
||||
auto textEdit = new QTextEdit;
|
||||
textEdit->setLineWrapMode(QTextEdit::NoWrap);
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiMultilineEntry,textEdit,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QProgressBar>
|
||||
|
||||
struct uiProgressBar : public uiQt5Control {};
|
||||
|
||||
void uiProgressBarSetValue(uiProgressBar *p, int value)
|
||||
{
|
||||
if (auto progressBar = uiValidateAndCastObjTo<QProgressBar>(p)) {
|
||||
return progressBar->setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
uiProgressBar *uiNewProgressBar(void)
|
||||
{
|
||||
auto progressBar = new QProgressBar;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiProgressBar,progressBar,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QRadioButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
struct uiRadioButtons : public uiQt5Control {};
|
||||
|
||||
void uiRadioButtonsAppend(uiRadioButtons *r, const char *text)
|
||||
{
|
||||
if (auto layout = uiValidateAndCastObjTo<QLayout>(r)) {
|
||||
auto radioButton = new QRadioButton(QString::fromUtf8(text));
|
||||
layout->addWidget(radioButton);
|
||||
if (layout->count() == 1) {
|
||||
radioButton->setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiRadioButtons *uiNewRadioButtons(void)
|
||||
{
|
||||
// TODO: check does this need a QButtonGroup or is the layout sufficent?
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiRadioButtons,layout,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
struct uiSeparator : public uiQt5Control {};
|
||||
|
||||
uiSeparator *uiNewHorizontalSeparator(void)
|
||||
{
|
||||
auto frame = new QFrame;
|
||||
frame->setFrameStyle(QFrame::HLine | QFrame::Sunken);
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiSeparator,frame,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QSlider>
|
||||
|
||||
struct uiSlider : public uiQt5Control {};
|
||||
|
||||
intmax_t uiSliderValue(uiSlider *s)
|
||||
{
|
||||
if (auto slider = uiValidateAndCastObjTo<QSlider>(s)) {
|
||||
return slider->value();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiSliderSetValue(uiSlider *s, intmax_t value)
|
||||
{
|
||||
if (auto slider = uiValidateAndCastObjTo<QSlider>(s)) {
|
||||
return slider->setValue((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data)
|
||||
{
|
||||
if (auto slider = uiValidateAndCastObjTo<QSlider>(s)) {
|
||||
QObject::connect(slider, &QSlider::valueChanged, slider, [f,s,data]{
|
||||
f(s,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiSlider *uiNewSlider(intmax_t min, intmax_t max)
|
||||
{
|
||||
auto slider = new QSlider(Qt::Horizontal);
|
||||
slider->setRange(qMin<int>(min,max), qMax<int>(min,max));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiSlider,slider,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
struct uiSpinbox : public uiQt5Control {};
|
||||
|
||||
intmax_t uiSpinboxValue(uiSpinbox *s)
|
||||
{
|
||||
if (auto spinBox = uiValidateAndCastObjTo<QSpinBox>(s)) {
|
||||
return spinBox->value();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiSpinboxSetValue(uiSpinbox *s, intmax_t value)
|
||||
{
|
||||
if (auto spinBox = uiValidateAndCastObjTo<QSpinBox>(s)) {
|
||||
spinBox->setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data)
|
||||
{
|
||||
if (auto spinBox = uiValidateAndCastObjTo<QSpinBox>(s)) {
|
||||
QObject::connect(spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), spinBox, [f,s,data]{
|
||||
f(s,data);
|
||||
}, Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max)
|
||||
{
|
||||
auto spinBox = new QSpinBox;
|
||||
spinBox->setRange(qMin(min,max),qMax(min,max));
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiSpinbox,spinBox,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
char *uiOpenFile(uiWindow *parent)
|
||||
{
|
||||
return uiQt5StrdupQString(QFileDialog::getOpenFileName(uiValidateAndCastObjTo<QWidget>(parent)));
|
||||
}
|
||||
|
||||
char *uiSaveFile(uiWindow *parent)
|
||||
{
|
||||
return uiQt5StrdupQString(QFileDialog::getSaveFileName(uiValidateAndCastObjTo<QWidget>(parent)));
|
||||
}
|
||||
|
||||
void uiMsgBox(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
QMessageBox::information(uiValidateAndCastObjTo<QWidget>(parent),
|
||||
QString::fromUtf8(title),
|
||||
QString::fromUtf8(description));
|
||||
}
|
||||
|
||||
void uiMsgBoxError(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
QMessageBox::warning(uiValidateAndCastObjTo<QWidget>(parent),
|
||||
QString::fromUtf8(title),
|
||||
QString::fromUtf8(description));
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QLayout>
|
||||
|
||||
struct uiTab : public uiQt5Control {};
|
||||
|
||||
static void tabInsertAt(QTabWidget *tabWidget, const char *name, int n, uiControl *child)
|
||||
{
|
||||
auto obj = uiValidateAndCastObjTo<QObject>(child);
|
||||
|
||||
if (auto layout = qobject_cast<QLayout*>(obj)) {
|
||||
auto widget = new QWidget;
|
||||
widget->setLayout(layout);
|
||||
tabWidget->insertTab(n, widget, QString::fromUtf8(name));
|
||||
} else if (auto widget = qobject_cast<QWidget*>(obj)) {
|
||||
tabWidget->insertTab(n, widget, QString::fromUtf8(name));
|
||||
} else {
|
||||
qWarning("object is neither layout nor widget");
|
||||
}
|
||||
}
|
||||
|
||||
void uiTabAppend(uiTab *t, const char *name, uiControl *child)
|
||||
{
|
||||
if (auto tabWidget = uiValidateAndCastObjTo<QTabWidget>(t)) {
|
||||
tabInsertAt(tabWidget,name,tabWidget->count(),child);
|
||||
}
|
||||
}
|
||||
|
||||
void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child)
|
||||
{
|
||||
if (auto tabWidget = uiValidateAndCastObjTo<QTabWidget>(t)) {
|
||||
tabInsertAt(tabWidget,name,qBound<int>(0, n, tabWidget->count()),child);
|
||||
}
|
||||
}
|
||||
|
||||
// reminder: badly named function, is remove not really a delete
|
||||
void uiTabDelete(uiTab *t, uintmax_t n)
|
||||
{
|
||||
if (auto tabWidget = uiValidateAndCastObjTo<QTabWidget>(t)) {
|
||||
int i = qBound<int>(0, n, tabWidget->count());
|
||||
if ((uintmax_t)i != n) {
|
||||
qWarning("Bad index: %llu", (unsigned long long)n);
|
||||
return;
|
||||
}
|
||||
auto widget = tabWidget->widget(i);
|
||||
tabWidget->removeTab(i);
|
||||
// check if it's a internal temp container widget we created
|
||||
if (uiFindQt5ControlForQObject(widget) == nullptr) {
|
||||
// widget is temp we created used as a container, separate the layout and then delete it
|
||||
|
||||
// problem once a layout assigned to widget, it's not so easy to get it back..?
|
||||
// one solution is to recreate the layout, but this needs some thought..
|
||||
// another solution is to never expose "layout" (uiBox), but always wrap them in containers..
|
||||
qWarning("FIXME: can't separate layout from temp widget");
|
||||
// delete widget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintmax_t uiTabNumPages(uiTab *t)
|
||||
{
|
||||
if (auto tabWidget = uiValidateAndCastObjTo<QTabWidget>(t)) {
|
||||
return tabWidget->count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uiTabMargined(uiTab *t, uintmax_t n)
|
||||
{
|
||||
qWarning("TODO %p, %d", (void*)t, (int)n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiTabSetMargined(uiTab *t, uintmax_t n, int margined)
|
||||
{
|
||||
qWarning("TODO %p, %d, %d", (void*)t, (int)n, margined);
|
||||
}
|
||||
|
||||
uiTab *uiNewTab(void)
|
||||
{
|
||||
auto tabWidget = new QTabWidget;
|
||||
|
||||
// note styling is being set in main.cpp -> styleSheet
|
||||
|
||||
return uiAllocQt5ControlType(uiTab,tabWidget,uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
static char *uiStrdupText(const char *s, int len)
|
||||
{
|
||||
// Reminder: could use strndup, but it has poor portability
|
||||
auto copy = (char *)malloc(len+1);
|
||||
if (copy) {
|
||||
memcpy(copy, s, len);
|
||||
copy[len] = 0;
|
||||
} else {
|
||||
qCritical("Failed to allocate %d+1 bytes", len);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
char *uiQt5StrdupQString(const QString &string)
|
||||
{
|
||||
if (string.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto utf8 = string.toUtf8();
|
||||
return uiStrdupText(utf8.constData(), utf8.length());
|
||||
}
|
||||
|
||||
void uiFreeText(char *t)
|
||||
{
|
||||
free(t);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
#ifndef __LIBUI_UIPRIV_QT5_HPP__
|
||||
#define __LIBUI_UIPRIV_QT5_HPP__
|
||||
|
||||
#include "../ui.h"
|
||||
#include "../ui_qt5.h"
|
||||
#include "../common/uipriv.h"
|
||||
|
||||
extern void initAlloc(void);
|
||||
extern void uninitAlloc(void);
|
||||
|
||||
// text
|
||||
class QString;
|
||||
char *uiQt5StrdupQString(const QString &string);
|
||||
|
||||
// control
|
||||
class QObject;
|
||||
struct uiQt5Control : public uiControl
|
||||
{
|
||||
QObject *qobject;
|
||||
enum {
|
||||
DefaultFlags=0,
|
||||
DeleteControlOnQObjectFree=0x1,
|
||||
SuppressValidatationNag=0x2
|
||||
};
|
||||
uint32_t flags;
|
||||
};
|
||||
extern uiQt5Control *uiValidateQt5Control(uiControl *control);
|
||||
|
||||
// control to qobject
|
||||
template <typename T=QObject>
|
||||
T *uiValidateAndCastObjTo(uiControl *control)
|
||||
{
|
||||
if (auto qt5Control = uiValidateQt5Control(control)) {
|
||||
return dynamic_cast<T*>(qt5Control->qobject);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
struct uiWindow : public uiQt5Control {};
|
||||
|
||||
// qobject to control
|
||||
extern uiQt5Control *uiFindQt5ControlForQObject(const QObject *qobject);
|
||||
|
||||
// alloc control
|
||||
extern uiQt5Control *uiAllocQt5Control(uint32_t typesig, const char *typenamestr, QObject *qobject, uint32_t flags = uiQt5Control::DefaultFlags);
|
||||
#define uiAllocQt5ControlType(type, widget, flags) static_cast<type*>(uiAllocQt5Control(type ## Signature, #type, widget, flags))
|
||||
|
||||
// menu
|
||||
class QMenuBar;
|
||||
extern QMenuBar *uiQt5FindMainMenuBar();
|
||||
|
||||
#endif // __LIBUI_UIPRIV_QT5_HPP__
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
#include "uipriv_qt5.hpp"
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QMenuBar>
|
||||
#include <QLayout>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class WindowWidget : public QMainWindow
|
||||
{
|
||||
public:
|
||||
std::function<void (QCloseEvent *event)> onClosing;
|
||||
void closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (onClosing) {
|
||||
onClosing(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
char *uiWindowTitle(uiWindow *w)
|
||||
{
|
||||
if (auto window = uiValidateAndCastObjTo<WindowWidget>(w)) {
|
||||
return uiQt5StrdupQString(window->windowTitle());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
if (auto window = uiValidateAndCastObjTo<WindowWidget>(w)) {
|
||||
window->setWindowTitle(QString::fromUtf8(title));
|
||||
}
|
||||
}
|
||||
|
||||
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
if (auto window = uiValidateAndCastObjTo<WindowWidget>(w)) {
|
||||
|
||||
if (f) {
|
||||
window->onClosing = [f,w,data](QCloseEvent *event){
|
||||
if (f(w,data)) {
|
||||
// eat the event and destroy the control
|
||||
// normally such behavior would be achived with
|
||||
// setAttribute(Qt::WA_DeleteOnClose, true)
|
||||
// but we need to behave consistently
|
||||
event->accept();
|
||||
w->Destroy(w);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// clear callback
|
||||
window->onClosing = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uiWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
if (auto window = uiValidateAndCastObjTo<WindowWidget>(w)) {
|
||||
auto obj = uiValidateAndCastObjTo<QObject>(child);
|
||||
if (window->centralWidget()) {
|
||||
window->centralWidget()->deleteLater();
|
||||
}
|
||||
if (auto layout = qobject_cast<QLayout*>(obj)) {
|
||||
auto widget = new QWidget;
|
||||
widget->setLayout(layout);
|
||||
window->setCentralWidget(widget);
|
||||
} else if (auto widget = qobject_cast<QWidget*>(obj)) {
|
||||
window->setCentralWidget(widget);
|
||||
} else {
|
||||
qWarning("object is neither layout nor widget");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int uiWindowMargined(uiWindow *w)
|
||||
{
|
||||
qWarning("TODO %p", (void*)w);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiWindowSetMargined(uiWindow *w, int margined)
|
||||
{
|
||||
qWarning("TODO %p, %d", (void*)w, margined);
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
auto window = new WindowWidget;
|
||||
|
||||
window->setWindowTitle(title);
|
||||
window->resize(width,height);
|
||||
|
||||
if (hasMenubar) {
|
||||
window->setMenuBar(uiQt5FindMainMenuBar());
|
||||
}
|
||||
|
||||
return uiAllocQt5ControlType(uiWindow, window, uiQt5Control::DeleteControlOnQObjectFree);
|
||||
}
|
Loading…
Reference in New Issue