diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb3cfa40..83713ce3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QWidget/qwidget_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QBoxLayout/qboxlayout_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QComboBox/qcombobox_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QFileDialog/qfiledialog_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QGridLayout/qgridlayout_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QDial/qdial_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QLabel/qlabel_wrap.cpp" @@ -83,6 +84,7 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QLabel/nlabel.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QCheckBox/ncheckbox.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QDial/ndial.hpp" + "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QFileDialog/nfiledialog.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QLineEdit/nlineedit.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QMainWindow/nmainwindow.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QProgressBar/nprogressbar.hpp" diff --git a/src/cpp/include/nodegui/QtWidgets/QFileDialog/nfiledialog.hpp b/src/cpp/include/nodegui/QtWidgets/QFileDialog/nfiledialog.hpp new file mode 100644 index 000000000..b5da8cacb --- /dev/null +++ b/src/cpp/include/nodegui/QtWidgets/QFileDialog/nfiledialog.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include + +#include "core/NodeWidget/nodewidget.h" +#include "napi.h" + +class NFileDialog : public QFileDialog, public NodeWidget { + public: + Q_OBJECT + NODEWIDGET_IMPLEMENTATIONS(QFileDialog) + using QFileDialog::QFileDialog; + + void connectWidgetSignalsToEventEmitter() { + // Qt Connects: Implement all signal connects here + QObject::connect( + this, &QFileDialog::currentChanged, [=](const QString &path) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "currentChanged"), + Napi::String::New(env, path.toStdString())}); + }); + + QObject::connect( + this, &QFileDialog::currentUrlChanged, [=](const QUrl &url) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "currentUrlChanged"), + Napi::String::New(env, url.toString().toStdString())}); + }); + + QObject::connect(this, &QFileDialog::directoryEntered, + [=](const QString &directory) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "directoryEntered"), + Napi::String::New(env, directory.toStdString())}); + }); + + QObject::connect( + this, &QFileDialog::directoryUrlEntered, [=](const QUrl &directory) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "directoryUrlEntered"), + Napi::String::New(env, directory.toString().toStdString())}); + }); + QObject::connect( + this, &QFileDialog::fileSelected, [=](const QString &file) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "fileSelected"), + Napi::String::New(env, file.toStdString())}); + }); + QObject::connect( + this, &QFileDialog::filesSelected, [=](const QStringList &selected) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + Napi::Array selectedNapi = Napi::Array::New(env, selected.size()); + for (int i = 0; i < selected.size(); i++) { + selectedNapi[i] = Napi::String::New(env, selected[i].toStdString()); + } + this->emitOnNode.Call( + {Napi::String::New(env, "filesSelected"), selectedNapi}); + }); + + QObject::connect( + this, &QFileDialog::filterSelected, [=](const QString &filter) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "filterSelected"), + Napi::String::New(env, filter.toStdString())}); + }); + + QObject::connect(this, &QFileDialog::urlSelected, [=](const QUrl &url) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "urlSelected"), + Napi::String::New(env, url.toString().toStdString())}); + }); + + QObject::connect( + this, &QFileDialog::urlsSelected, [=](const QList &urls) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + Napi::Array urlsNapi = Napi::Array::New(env, urls.size()); + for (int i = 0; i < urls.size(); i++) { + urlsNapi[i] = + Napi::String::New(env, urls[i].toString().toStdString()); + } + this->emitOnNode.Call( + {Napi::String::New(env, "urlsSelected"), urlsNapi}); + }); + } +}; diff --git a/src/cpp/include/nodegui/QtWidgets/QFileDialog/qfiledialog_wrap.h b/src/cpp/include/nodegui/QtWidgets/QFileDialog/qfiledialog_wrap.h new file mode 100644 index 000000000..cc7e51597 --- /dev/null +++ b/src/cpp/include/nodegui/QtWidgets/QFileDialog/qfiledialog_wrap.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include + +#include "Extras/Utils/nutils.h" +#include "QtWidgets/QFileDialog/nfiledialog.hpp" +#include "QtWidgets/QWidget/qwidget_macro.h" + +class QFileDialogWrap : public Napi::ObjectWrap { + private: + QPointer instance; + + public: + static Napi::Object init(Napi::Env env, Napi::Object exports); + QFileDialogWrap(const Napi::CallbackInfo& info); + ~QFileDialogWrap(); + NFileDialog* getInternalInstance(); + // class constructor + static Napi::FunctionReference constructor; + // wrapped methods + Napi::Value open(const Napi::CallbackInfo& info); + Napi::Value supportedSchemes(const Napi::CallbackInfo& info); + Napi::Value setSupportedSchemes(const Napi::CallbackInfo& info); + Napi::Value labelText(const Napi::CallbackInfo& info); + Napi::Value setLabelText(const Napi::CallbackInfo& info); + Napi::Value setOption(const Napi::CallbackInfo& info); + + QWIDGET_WRAPPED_METHODS_DECLARATION +}; diff --git a/src/cpp/lib/QtWidgets/QFileDialog/qfiledialog_wrap.cpp b/src/cpp/lib/QtWidgets/QFileDialog/qfiledialog_wrap.cpp new file mode 100644 index 000000000..38dc28909 --- /dev/null +++ b/src/cpp/lib/QtWidgets/QFileDialog/qfiledialog_wrap.cpp @@ -0,0 +1,132 @@ +#include "QtWidgets/QFileDialog/qfiledialog_wrap.h" + +#include + +#include "Extras/Utils/nutils.h" +#include "QtWidgets/QWidget/qwidget_wrap.h" + +Napi::FunctionReference QFileDialogWrap::constructor; + +Napi::Object QFileDialogWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QFileDialog"; + Napi::Function func = DefineClass( + env, CLASSNAME, + {InstanceMethod("open", &QFileDialogWrap::open), + InstanceMethod("supportedSchemes", &QFileDialogWrap::supportedSchemes), + InstanceMethod("setSupportedSchemes", + &QFileDialogWrap::setSupportedSchemes), + InstanceMethod("labelText", &QFileDialogWrap::labelText), + InstanceMethod("setLabelText", &QFileDialogWrap::setLabelText), + InstanceMethod("setOption", &QFileDialogWrap::setOption), + QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QFileDialogWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +NFileDialog* QFileDialogWrap::getInternalInstance() { return this->instance; } +QFileDialogWrap::~QFileDialogWrap() { extrautils::safeDelete(this->instance); } + +QFileDialogWrap::QFileDialogWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + if (info.Length() == 4) { + Napi::Object parentObject = info[0].As(); + QWidgetWrap* parentWidgetWrap = + Napi::ObjectWrap::Unwrap(parentObject); + QWidget* parent = parentWidgetWrap->getInternalInstance(); + QString caption = + QString::fromUtf8(info[1].As().Utf8Value().c_str()); + QString directory = + QString::fromUtf8(info[2].As().Utf8Value().c_str()); + QString filter = + QString::fromUtf8(info[3].As().Utf8Value().c_str()); + this->instance = new NFileDialog(parent, caption, directory, filter); + } else if (info.Length() == 0) { + this->instance = new NFileDialog(); + } else { + Napi::TypeError::New(env, "Wrong number of arguments") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureQWidget( + this->getInternalInstance(), this->getInternalInstance()->getFlexNode(), + false); +} +Napi::Value QFileDialogWrap::open(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + this->instance->open(); + return env.Null(); +} + +Napi::Value QFileDialogWrap::supportedSchemes(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + QStringList schemes = this->instance->supportedSchemes(); + Napi::Array schemesNapi = Napi::Array::New(env, schemes.size()); + for (int i = 0; i < schemes.size(); i++) { + schemesNapi[i] = Napi::String::New(env, schemes[i].toStdString()); + } + + return schemesNapi; +} + +Napi::Value QFileDialogWrap::setSupportedSchemes( + const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + Napi::Array schemesNapi = info[0].As(); + QList list; + for (int i = 0; i < schemesNapi.Length(); i++) { + Napi::Value schemeNapi = schemesNapi[i]; + list.append(schemeNapi.As().Utf8Value().c_str()); + } + QStringList schemes = QStringList(list); + + this->instance->setSupportedSchemes(schemes); + return env.Null(); +} + +Napi::Value QFileDialogWrap::labelText(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + int labelInt = info[0].As().Int32Value(); + QFileDialog::DialogLabel label = + static_cast(labelInt); + QString labelString = this->instance->labelText(label); + + return Napi::String::New(env, labelString.toStdString()); +} + +Napi::Value QFileDialogWrap::setLabelText(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + int labelInt = info[0].As().Int32Value(); + QFileDialog::DialogLabel label = + static_cast(labelInt); + std::string textString = info[1].As().Utf8Value(); + QString text = QString::fromUtf8(textString.c_str()); + + this->instance->setLabelText(label, text); + return env.Null(); +} + +Napi::Value QFileDialogWrap::setOption(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + int optionInt = info[0].As().Int32Value(); + QFileDialog::Option option = static_cast(optionInt); + bool on = info[1].As().Value(); + + this->instance->setOption(option, on); + return env.Null(); +} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 479e0c06d..bc27334fc 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -20,6 +20,7 @@ #include "QtWidgets/QCheckBox/qcheckbox_wrap.h" #include "QtWidgets/QComboBox/qcombobox_wrap.h" #include "QtWidgets/QDial/qdial_wrap.h" +#include "QtWidgets/QFileDialog/qfiledialog_wrap.h" #include "QtWidgets/QGridLayout/qgridlayout_wrap.h" #include "QtWidgets/QGroupBox/qgroupbox_wrap.h" #include "QtWidgets/QLabel/qlabel_wrap.h" @@ -64,6 +65,7 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { QCursorWrap::init(env, exports); QComboBoxWrap::init(env, exports); QBoxLayoutWrap::init(env, exports); + QFileDialogWrap::init(env, exports); QGridLayoutWrap::init(env, exports); QGroupBoxWrap::init(env, exports); FlexLayoutWrap::init(env, exports); diff --git a/src/demo.ts b/src/demo.ts index 40999c1c8..df96d0a3a 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -1,23 +1,33 @@ -import { QMainWindow, QPushButton, QLabel, QWidget, FlexLayout, QComboBox, QComboBoxEvents } from './index'; +import { + QMainWindow, + QPushButton, + QLabel, + QWidget, + FlexLayout, + QFileDialog, + QFileDialogEvents, + FileMode, + DialogLabel, +} from './index'; const win = new QMainWindow(); const center = new QWidget(); const button = new QPushButton(); -button.setText('Hello'); +button.setText('Open File Dialog'); const hello = new QLabel(); hello.setText('hello text'); -const combo = new QComboBox(); -combo.addItems(['text1', 'text2', 'text3']) -combo.insertItems(1,['insert1', 'insert2', 'insert3']) -combo.addEventListener(QComboBoxEvents.currentTextChanged, (text)=>hello.setText(text)) +const file = new QFileDialog(center, 'Select File', `${process.env.HOME}`); +file.setFileMode(FileMode.ExistingFiles); +file.setLabelText(DialogLabel.Accept, 'New Accept Label!'); +file.addEventListener(QFileDialogEvents.filesSelected, (selected: string[]) => console.log('text', selected)); +button.addEventListener('clicked', () => file.open()); center.setLayout(new FlexLayout()); center.layout?.addWidget(button); center.layout?.addWidget(hello); -center.layout?.addWidget(combo); win.setCentralWidget(center); win.show(); diff --git a/src/index.ts b/src/index.ts index 05b16627c..d1f9dcd60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ export { QWidget, QWidgetEvents } from './lib/QtWidgets/QWidget'; export { QCheckBox, QCheckBoxEvents } from './lib/QtWidgets/QCheckBox'; export { QLabel, QLabelEvents } from './lib/QtWidgets/QLabel'; export { QDial, QDialEvents } from './lib/QtWidgets/QDial'; +export { QFileDialog, QFileDialogEvents } from './lib/QtWidgets/QFileDialog'; export { QLineEdit, QLineEditEvents, EchoMode } from './lib/QtWidgets/QLineEdit'; export { QMainWindow, QMainWindowEvents } from './lib/QtWidgets/QMainWindow'; export { QProgressBar, QProgressBarEvents } from './lib/QtWidgets/QProgressBar'; diff --git a/src/lib/QtEnums/AcceptMode/index.ts b/src/lib/QtEnums/AcceptMode/index.ts new file mode 100644 index 000000000..d87525ad8 --- /dev/null +++ b/src/lib/QtEnums/AcceptMode/index.ts @@ -0,0 +1,4 @@ +export enum AcceptMode { + AcceptOpen = 0, + AcceptSave = 1, +} diff --git a/src/lib/QtEnums/DialogLabel/index.ts b/src/lib/QtEnums/DialogLabel/index.ts new file mode 100644 index 000000000..f8acd47b8 --- /dev/null +++ b/src/lib/QtEnums/DialogLabel/index.ts @@ -0,0 +1,7 @@ +export enum DialogLabel { + LookIn = 0, + FileName = 1, + FileType = 2, + Accept = 3, + Reject = 4, +} diff --git a/src/lib/QtEnums/FileMode/index.ts b/src/lib/QtEnums/FileMode/index.ts new file mode 100644 index 000000000..d95ccf37c --- /dev/null +++ b/src/lib/QtEnums/FileMode/index.ts @@ -0,0 +1,6 @@ +export enum FileMode { + AnyFile = 0, + ExistingFile = 1, + Directory = 2, + ExistingFiles = 3, +} diff --git a/src/lib/QtEnums/Option/index.ts b/src/lib/QtEnums/Option/index.ts new file mode 100644 index 000000000..b8f90d686 --- /dev/null +++ b/src/lib/QtEnums/Option/index.ts @@ -0,0 +1,10 @@ +export enum Option { + ShowDirsOnly = 0x00000001, + DontResolveSymlinks = 0x00000002, + DontConfirmOverwrite = 0x00000004, + DontUseNativeDialog = 0x00000010, + ReadOnly = 0x00000020, + HideNameFilterDetails = 0x00000040, + DontUseSheet = 0x00000008, + DontUseCustomDirectoryIcons = 0x00000080, +} diff --git a/src/lib/QtEnums/ViewMode/index.ts b/src/lib/QtEnums/ViewMode/index.ts new file mode 100644 index 000000000..407d4b26b --- /dev/null +++ b/src/lib/QtEnums/ViewMode/index.ts @@ -0,0 +1,4 @@ +export enum ViewMode { + Detail = 0, + List = 1, +} diff --git a/src/lib/QtEnums/index.ts b/src/lib/QtEnums/index.ts index 874256fcc..826061e2f 100644 --- a/src/lib/QtEnums/index.ts +++ b/src/lib/QtEnums/index.ts @@ -1,3 +1,4 @@ +export { AcceptMode } from './AcceptMode'; export { AlignmentFlag } from './AlignmentFlag'; export { AnchorPoint } from './AnchorPoint'; export { ApplicationAttribute } from './ApplicationAttribute'; @@ -19,12 +20,14 @@ export { CursorMoveStyle } from './CursorMoveStyle'; export { CursorShape } from './CursorShape'; export { DateFormat } from './DateFormat'; export { DayOfWeek } from './DayOfWeek'; +export { DialogLabel } from './DialogLabel'; export { Direction } from './Direction'; export { DockWidgetArea } from './DockWidgetArea'; export { DropAction } from './DropAction'; export { Edge } from './Edge'; export { EnterKeyType } from './EnterKeyType'; export { EventPriority } from './EventPriority'; +export { FileMode } from './FileMode'; export { FillRule } from './FillRule'; export { FindChildOption } from './FindChildOption'; export { FocusPolicy } from './FocusPolicy'; @@ -52,6 +55,7 @@ export { MouseEventFlag } from './MouseEventFlag'; export { MouseEventSource } from './MouseEventSource'; export { NativeGestureType } from './NativeGestureType'; export { NavigationMode } from './NavigationMode'; +export { Option } from './Option'; export { Orientation } from './Orientation'; export { ScreenOrientation } from './ScreenOrientation'; export { ScrollBarPolicy } from './ScrollBarPolicy'; @@ -75,6 +79,7 @@ export { ToolButtonStyle } from './ToolButtonStyle'; export { TouchPointState } from './TouchPointState'; export { TransformationMode } from './TransformationMode'; export { UIEffect } from './UIEffect'; +export { ViewMode } from './ViewMode'; export { WhiteSpaceMode } from './WhiteSpaceMode'; export { WidgetAttribute } from './WidgetAttribute'; export { WindowFrameSection } from './WindowFrameSection'; diff --git a/src/lib/QtWidgets/QComboBox.ts b/src/lib/QtWidgets/QComboBox.ts index b89d6927f..8bcc79cd9 100644 --- a/src/lib/QtWidgets/QComboBox.ts +++ b/src/lib/QtWidgets/QComboBox.ts @@ -32,7 +32,7 @@ export class QComboBox extends NodeWidget { addItems(texts: string[]): void { this.native.addItems(texts); } - insertItems(index:number, texts: string[]): void { + insertItems(index: number, texts: string[]): void { this.native.insertItems(index, texts); } currentIndex(): number { diff --git a/src/lib/QtWidgets/QFileDialog.ts b/src/lib/QtWidgets/QFileDialog.ts new file mode 100644 index 000000000..aa01691c1 --- /dev/null +++ b/src/lib/QtWidgets/QFileDialog.ts @@ -0,0 +1,77 @@ +import addon from '../utils/addon'; +import { NodeWidget } from './QWidget'; +import { BaseWidgetEvents } from '../core/EventWidget'; +import { NativeElement } from '../core/Component'; +import { AcceptMode, DialogLabel, FileMode, Option, ViewMode } from '../QtEnums'; +export const QFileDialogEvents = Object.freeze({ + currentChanged: 'currentChanged', + currentUrlChanged: 'currentUrlChanged', + directoryEntered: 'directoryEntered', + directoryUrlEntered: 'directoryUrlEntered', + fileSelected: 'fileSelected', + filesSelected: 'filesSelected', + filterSelected: 'filterSelected', + urlSelected: 'urlSelected', + urlsSelected: 'urlsSelected', + ...BaseWidgetEvents, +}); + +export class QFileDialog extends NodeWidget { + native: NativeElement; + constructor(parent: NodeWidget | undefined = undefined, caption = 'Select File', directory = '', filter = '') { + let native; + if (parent) { + native = new addon.QFileDialog(parent.native, caption, directory, filter); + } else { + native = new addon.QFileDialog(); + } + super(native); + this.native = native; + this.nodeParent = parent; + } + open(): void { + this.native.open(); + } + supportedSchemes(): string[] { + return this.native.supportedSchemes(); + } + setSupportedSchemes(schemes: string[]): void { + this.native.setSupportedSchemes(schemes); + } + labelText(label: DialogLabel): string { + return this.native.labelText(label); + } + setLabelText(label: DialogLabel, text: string): void { + this.native.setLabelText(label, text); + } + setOption(option: Option, on = true): void { + this.native.setOption(option, on); + } + acceptMode(): AcceptMode { + return this.property('acceptMode').toInt(); + } + defaultSuffix(): string { + return this.property('defaultSuffix').toString(); + } + fileMode(): FileMode { + return this.property('fileMode').toInt(); + } + options(): Option { + return this.property('options').toInt(); + } + viewMode(): ViewMode { + return this.property('viewMode').toInt(); + } + setAcceptMode(acceptMode: AcceptMode): void { + this.setProperty('acceptMode', acceptMode); + } + setDefaultSuffix(defaultSuffix: string): void { + this.setProperty('defaultSuffix', defaultSuffix); + } + setFileMode(fileMode: FileMode): void { + this.setProperty('fileMode', fileMode); + } + setOptions(options: Option): void { + this.setProperty('options', options); + } +}