Adds inline styling support and other native methods

This commit is contained in:
Atul R 2019-08-10 11:21:36 +02:00
parent c3de181e32
commit 4f9e27d36a
11 changed files with 222 additions and 36 deletions

22
demo.ts
View File

@ -3,16 +3,20 @@ import {
QWidget,
QLabel,
FlexLayout,
StyleSheet,
QPushButton
} from "./src/lib/index";
const win = new QMainWindow();
//-------------------------------
const centralWidget = new QWidget();
centralWidget.setObjectName("myroot");
const rootLayout = new FlexLayout();
centralWidget.setLayout(rootLayout);
const view = new QWidget();
const viewLayout = new FlexLayout();
view.setInlineStyle(
`margin-top:20px; background: url(${"/Users/atulr/Downloads/photo.jpeg"});`
);
view.setLayout(viewLayout);
//--------------------------------------
const label = new QLabel();
label.setObjectName("mylabel");
@ -22,22 +26,20 @@ const btn = new QPushButton();
btn.setText("Yo button");
btn.setObjectName("btn");
//--------------------------------------
rootLayout.addWidget(label);
rootLayout.addWidget(btn);
viewLayout.addWidget(label);
viewLayout.addWidget(btn);
rootLayout.addWidget(view);
win.setCentralWidget(centralWidget);
const winStyleSheet = StyleSheet.create(`
#myroot {
background-color: #009688;
}
const winStyleSheet = `
#mylabel {
font-size: 16px;
font-weight: bold;
margin-top: 30px;
margin-top: 40px;
}
#btn {
margin-top: 30px;
}
`);
`;
win.setStyleSheet(winStyleSheet);
win.show();
win.resize(400, 500);

View File

@ -82,6 +82,10 @@ Sets the property that holds the widget's style sheet. It calls the native metho
- `styleSheet` string - String which holds the widget's style sheet. Make sure you create this string using `StyleSheet.create()`
#### `widget.styleSheet()`
Gets the property that holds the widget's style sheet. It calls the native method [QWidget: styleSheet](https://doc.qt.io/qt-5/qwidget.html#styleSheet-prop).
#### `widget.hide()`
Hides the widget and its children. It calls the native method [QWidget: hide](https://doc.qt.io/qt-5/qwidget.html#hide).
@ -92,6 +96,10 @@ Sets the object name of the widget in Qt. It calls the native method [QObject: s
- `objectName` string - String which holds the widget's object name.
#### `widget.objectName()`
Gets the property that holds the widget's object name. It calls the native method [QObject: setObjectName](https://doc.qt.io/qt-5/qobject.html#objectName-prop).
#### `widget.setMouseTracking(isMouseTracked)`
Sets the property that tells whether mouseTracking is enabled for the widget. It calls the native method [QWidget: mouseTracking](https://doc.qt.io/qt-5/qwidget.html#mouseTracking-prop).
@ -140,3 +148,16 @@ returns the current widget size. It calls the native method [QWidget: size](http
#### `widget.updateGeometry()`
Notifies the layout system that this widget has changed and may need to change geometry.
#### `widget.setAttribute(attributeName, switchOn)`
Sets the attribute attribute on this widget if on is true; otherwise clears the attribute. It calls the native method [QWidget: setAttribute](https://doc.qt.io/qt-5/qwidget.html#setAttribute).
- `attributeName` WidgetAttribute - Enum from WidgetAttribute.
- `switchOn` - set it to true if you want to enable an attribute.
#### `widget.testAttribute(attributeName)`
Returns true if attribute attribute is set on this widget; otherwise returns false. It calls the native method [QWidget: testAttribute](https://doc.qt.io/qt-5/qwidget.html#testAttribute).
- `attributeName` WidgetAttribute - Enum from WidgetAttribute.

View File

@ -8,7 +8,6 @@ import {
import { QLabel } from "../../src/lib/QtWidgets/QLabel";
import { BaseWidgetEvents } from "../../src/lib/core/EventWidget";
import { QKeyEvent } from "../../src/lib/QtGui/QEvent/QKeyEvent";
import { StyleSheet } from "../../src/lib";
const globals = global as any;
@ -47,8 +46,7 @@ win.addEventListener(BaseWidgetEvents.KeyRelease, nativeEvent => {
});
rootView.setObjectName("rootView"); //This is like ids in web world
win.setCentralWidget(rootView);
const rootStyleSheet = StyleSheet.create(
`
const rootStyleSheet = `
* {
font-size: 20px;
color: white;
@ -111,10 +109,9 @@ QPushButton:pressed {
#row2 QPushButton:pressed, #row2 QPushButton:pressed, #row3 QPushButton:pressed, #row4 QPushButton:pressed {
background: grey;
}
`
);
`;
const operatorStyleSheet = StyleSheet.create(`
const operatorStyleSheet = `
QPushButton {
background: #FD8D0E;
}
@ -122,7 +119,7 @@ QPushButton {
QPushButton:pressed {
background: grey;
}
`);
`;
rootView.setStyleSheet(rootStyleSheet);
const rootViewFlexLayout = new FlexLayout();

5
package-lock.json generated
View File

@ -407,6 +407,11 @@
"which": "^1.2.9"
}
},
"cuid": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.6.tgz",
"integrity": "sha512-ZFp7PS6cSYMJNch9fc3tyHdE4T8TDo3Y5qAxb0KSA9mpiYDo7z9ql1CznFuuzxea9STVIDy0tJWm2lYiX2ZU1Q=="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",

View File

@ -27,6 +27,7 @@
"dependencies": {
"@nodegui/test": "^0.0.10",
"bindings": "^1.5.0",
"cuid": "^2.1.6",
"node-addon-api": "^1.6.3",
"node-gyp": "^4.0.0",
"postcss-nodegui-autoprefixer": "0.0.7"

View File

@ -1,6 +1,6 @@
const path = require("path");
const fs = require("fs");
const process = require("child_process");
const childProcess = require("child_process");
const ROOT_DIR = path.resolve(__dirname, "../");
const MOC_AUTOGEN_DIR = path.resolve(ROOT_DIR, "src/cpp/autogen");
@ -53,7 +53,7 @@ const main = () => {
includeFilePath
);
console.log(command);
process.exec(command, error => {
childProcess.exec(command, {}, error => {
if (error) {
console.error(`exec error: ${error}`);
return;

View File

@ -57,7 +57,12 @@ Napi::Value setStyleSheet(const Napi::CallbackInfo& info){ \
this->instance->setStyleSheet(style.c_str()); \
return env.Null(); \
} \
\
Napi::Value styleSheet(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
QString stylesheet = this->instance->styleSheet(); \
return Napi::String::New(env, stylesheet.toStdString()); \
} \
Napi::Value hide(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -72,6 +77,12 @@ Napi::Value setObjectName(const Napi::CallbackInfo& info){ \
this->instance->setObjectName(QString::fromStdString(objectName.Utf8Value())); \
return env.Null(); \
} \
Napi::Value objectName(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
QString objectName = this->instance->objectName(); \
return Napi::String::New(env, objectName.toStdString()); \
} \
Napi::Value setMouseTracking(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -137,6 +148,21 @@ Napi::Value size(const Napi::CallbackInfo& info){ \
sizeObj.Set("height", size.height()); \
return sizeObj; \
} \
Napi::Value setAttribute(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
int attributeId = info[0].As<Napi::Number>().Int32Value(); \
bool switchOn = info[1].As<Napi::Boolean>().Value(); \
this->instance->setAttribute(static_cast<Qt::WidgetAttribute>(attributeId), switchOn); \
return env.Null(); \
} \
Napi::Value testAttribute(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
int attributeId = info[0].As<Napi::Number>().Int32Value(); \
bool isOn = this->instance->testAttribute(static_cast<Qt::WidgetAttribute>(attributeId)); \
return Napi::Boolean::New(env, isOn); \
} \
#endif //QWIDGET_WRAPPED_METHODS_DECLARATION
@ -151,8 +177,10 @@ Napi::Value size(const Napi::CallbackInfo& info){ \
InstanceMethod("close",&WidgetWrapName::close), \
InstanceMethod("setLayout",&WidgetWrapName::setLayout), \
InstanceMethod("setStyleSheet",&WidgetWrapName::setStyleSheet), \
InstanceMethod("styleSheet",&WidgetWrapName::styleSheet), \
InstanceMethod("hide",&WidgetWrapName::hide), \
InstanceMethod("setObjectName",&WidgetWrapName::setObjectName), \
InstanceMethod("objectName",&WidgetWrapName::objectName), \
InstanceMethod("setMouseTracking",&WidgetWrapName::setMouseTracking), \
InstanceMethod("setEnabled",&WidgetWrapName::setEnabled), \
InstanceMethod("setFixedSize",&WidgetWrapName::setFixedSize), \
@ -162,5 +190,7 @@ Napi::Value size(const Napi::CallbackInfo& info){ \
InstanceMethod("update",&WidgetWrapName::update), \
InstanceMethod("updateGeometry",&WidgetWrapName::updateGeometry), \
InstanceMethod("size",&WidgetWrapName::size), \
InstanceMethod("setAttribute",&WidgetWrapName::setAttribute), \
InstanceMethod("testAttribute",&WidgetWrapName::testAttribute), \
#endif // QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE

View File

@ -22,7 +22,6 @@ NWidget* QWidgetWrap::getInternalInstance() {
QWidgetWrap::QWidgetWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QWidgetWrap>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if(info.Length() == 1) {
Napi::Object parentObject = info[0].As<Napi::Object>();
QWidgetWrap* parentWidgetWrap = Napi::ObjectWrap<QWidgetWrap>::Unwrap(parentObject);

View File

@ -3,3 +3,89 @@ export enum AspectRatioMode {
"KeepAspectRatio",
"KeepAspectRatioByExpanding"
}
export enum WidgetAttribute {
WA_AcceptDrops = 78,
WA_AlwaysShowToolTips = 84,
WA_ContentsPropagated = 3,
WA_CustomWhatsThis = 47,
WA_DeleteOnClose = 55,
WA_Disabled = 0,
WA_DontShowOnScreen = 103,
WA_ForceDisabled = 32,
WA_ForceUpdatesDisabled = 59,
WA_GroupLeader = 72,
WA_Hover = 74,
WA_InputMethodEnabled = 14,
WA_KeyboardFocusChange = 77,
WA_KeyCompression = 33,
WA_LayoutOnEntireRect = 48,
WA_LayoutUsesWidgetRect = 92,
WA_MacNoClickThrough = 12,
WA_MacOpaqueSizeGrip = 85,
WA_MacShowFocusRect = 88,
WA_MacNormalSize = 89,
WA_MacSmallSize = 90,
WA_MacMiniSize = 91,
WA_MacVariableSize = 102,
WA_MacBrushedMetal = 46,
WA_Mapped = 11,
WA_MouseNoMask = 71,
WA_MouseTracking = 2,
WA_Moved = 43,
WA_MSWindowsUseDirect3D = 94,
WA_NoChildEventsForParent = 58,
WA_NoChildEventsFromChildren = 39,
WA_NoMouseReplay = 54,
WA_NoMousePropagation = 73,
WA_TransparentForMouseEvents = 51,
WA_NoSystemBackground = 9,
WA_OpaquePaintEvent = 4,
WA_OutsideWSRange = 49,
WA_PaintOnScreen = 8,
WA_PaintUnclipped = 52,
WA_PendingMoveEvent = 34,
WA_PendingResizeEvent = 35,
WA_QuitOnClose = 76,
WA_Resized = 42,
WA_RightToLeft = 56,
WA_SetCursor = 38,
WA_SetFont = 37,
WA_SetPalette = 36,
WA_SetStyle = 86,
WA_ShowModal = 70,
WA_StaticContents = 5,
WA_StyleSheet = 97,
WA_StyleSheetTarget = 131,
WA_TabletTracking = 129,
WA_TranslucentBackground = 120,
WA_UnderMouse = 1,
WA_UpdatesDisabled = 10,
WA_WindowModified = 41,
WA_WindowPropagation = 80,
WA_MacAlwaysShowToolWindow = 96,
WA_SetLocale = 87,
WA_StyledBackground = 93,
WA_ShowWithoutActivating = 98,
WA_NativeWindow = 100,
WA_DontCreateNativeAncestors = 101,
WA_X11NetWmWindowTypeDesktop = 104,
WA_X11NetWmWindowTypeDock = 105,
WA_X11NetWmWindowTypeToolBar = 106,
WA_X11NetWmWindowTypeMenu = 107,
WA_X11NetWmWindowTypeUtility = 108,
WA_X11NetWmWindowTypeSplash = 109,
WA_X11NetWmWindowTypeDialog = 110,
WA_X11NetWmWindowTypeDropDownMenu = 111,
WA_X11NetWmWindowTypePopupMenu = 112,
WA_X11NetWmWindowTypeToolTip = 113,
WA_X11NetWmWindowTypeNotification = 114,
WA_X11NetWmWindowTypeCombo = 115,
WA_X11NetWmWindowTypeDND = 116,
WA_MacFrameworkScaled = 117,
WA_AcceptTouchEvents = 121,
WA_TouchPadAcceptSingleTouchEvents = 123,
WA_X11DoNotAcceptFocus = 126,
WA_AlwaysStackOnTop = 128,
WA_ContentsMarginsRespectsSafeArea = 13
}

View File

@ -3,11 +3,17 @@ import { NodeLayout } from "../../QtWidgets/QLayout";
import { EventWidget, BaseWidgetEvents } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { FlexLayout } from "../../core/FlexLayout";
import { WidgetAttribute } from "../../QtEnums";
import {
applyStyleSheet,
StyleSheet,
prepareInlineStyleSheet
} from "../../core/Style/StyleSheet";
// All Widgets should extend from NodeWidget
// Implement all native QWidget methods here so that all widgets get access to those aswell
export abstract class NodeWidget extends EventWidget {
type = "widget";
layout?: NodeLayout;
type: string = "widget";
show = () => {
this.native.show();
};
@ -26,14 +32,16 @@ export abstract class NodeWidget extends EventWidget {
this.native.setLayout(parentLayout.native);
this.layout = parentLayout;
};
setStyleSheet = async (style: string | Promise<string>) => {
this.native.setStyleSheet(await style);
setTimeout(() => {
if (this.layout) {
this.layout.invalidate(); // Hackfix: To trigger recalculation
this.layout.update();
}
}, 20);
setStyleSheet = async (styleSheet: string) => {
const preparedSheet = await StyleSheet.create(styleSheet);
await applyStyleSheet(this, preparedSheet);
};
setInlineStyle = async (style: string) => {
const preparedSheet = await prepareInlineStyleSheet(this, style);
await applyStyleSheet(this, preparedSheet);
};
styleSheet = (): string => {
return this.native.styleSheet();
};
hide = () => {
this.native.hide();
@ -41,6 +49,9 @@ export abstract class NodeWidget extends EventWidget {
setObjectName = (objectName: string) => {
this.native.setObjectName(objectName);
};
objectName = (): string => {
return this.native.objectName();
};
setMouseTracking = (isMouseTracked: boolean) => {
this.native.setMouseTracking(isMouseTracked);
};
@ -68,6 +79,12 @@ export abstract class NodeWidget extends EventWidget {
size = (): { width: number; height: number } => {
return this.native.size();
};
setAttribute = (attribute: WidgetAttribute, switchOn: boolean) => {
return this.native.setAttribute(attribute, switchOn);
};
testAttribute = (attribute: WidgetAttribute): boolean => {
return this.native.testAttribute(attribute);
};
}
export class QWidget extends NodeWidget {

View File

@ -1,12 +1,11 @@
import postcss from "postcss";
import autoprefixer from "postcss-nodegui-autoprefixer";
import cuid from "cuid";
import nodeguiAutoPrefixer from "postcss-nodegui-autoprefixer";
import { NodeWidget } from "../../QtGui/QWidget";
export class StyleSheet {
static create = async (cssString: string): Promise<string> => {
const { css } = await postcss([autoprefixer()])
.process(cssString, {
from: undefined
})
const { css } = await postcss([nodeguiAutoPrefixer()])
.process(cssString, { from: undefined })
.catch(err => {
console.warn(`Error autoprefixing`, err);
return { css: cssString };
@ -14,3 +13,32 @@ export class StyleSheet {
return css;
};
}
export const prepareInlineStyleSheet = async (
widget: NodeWidget,
rawStyle: string
) => {
const inlineStyle = await StyleSheet.create(rawStyle);
// Make sure to not calculate ObjectName is the same pass of event loop as other props (incase of react) since the order will matter in that case
// So doing it in multiple passes of event loop allows objectName to be set before using it. The above await solves it.
let cssId = widget.objectName();
if (!cssId) {
cssId = cuid();
widget.setObjectName(cssId);
}
return `
#${cssId} {
${inlineStyle}
}
`;
};
export const applyStyleSheet = async (
widget: NodeWidget,
styleSheet: string
) => {
widget.native.setStyleSheet(styleSheet);
setTimeout(() => {
widget.layout ? widget.layout.update() : widget.update();
}, 20);
};