diff --git a/src/components/Action/RNAction.ts b/src/components/Action/RNAction.ts new file mode 100644 index 00000000..ba44cd5c --- /dev/null +++ b/src/components/Action/RNAction.ts @@ -0,0 +1,146 @@ +import { + QAction, + QIcon, + QActionSignals, + Component, + QFont, + QShortcut, + QKeySequence, + ShortcutContext, +} from "@nodegui/nodegui"; +import { RNComponent, RNProps } from "../config"; +import { throwUnsupported } from "../../utils/helpers"; + +export interface ActionProps extends RNProps { + /** + * Sets whether the action is a checkable action. [QAction: setCheckable](https://docs.nodegui.org/docs/api/generated/classes/qaction#setcheckable) + */ + checkable?: boolean; + + /** + * Sets whether the action is checked. [QAction: setChecked](https://docs.nodegui.org/docs/api/generated/classes/qaction#setchecked) + */ + checked?: boolean; + + /** + * Sets whether the action is enabled. [QAction: setEnabled](https://docs.nodegui.org/docs/api/generated/classes/qaction#setenabled) + */ + enabled?: boolean; + + /** + * Sets a font for the action. [QAction: setFont](https://docs.nodegui.org/docs/api/generated/classes/qaction#setfont) + */ + font?: QFont; + + /** + * Sets an icon for the action. [QSystemTrayIcon: setIcon](https://docs.nodegui.org/docs/api/generated/classes/qsystemtrayicon#seticon) + */ + icon?: QIcon; + + /** + * Sets the object name (id) of the widget in Qt. Object name can be analogous to id of an element in the web world. Using the objectName of the widget one can reference it in the Qt's stylesheet much like what we do with id in the web world. [QWidget: setObjectName](https://docs.nodegui.org/docs/api/NodeWidget#widgetsetobjectnameobjectname) + */ + id?: string; + + /** + * Prop to set the event listener map. See [Handlong Events](/docs/guides/handle-events) + */ + on?: Partial; + + /** + * Sets whether this action will be considered a separator. [QAction: setSeparator](https://docs.nodegui.org/docs/api/generated/classes/qaction#setseparator) + */ + separator?: boolean; + + /** + * Sets the action's primary shortcut key. [QAction: setShortcut](https://docs.nodegui.org/docs/api/generated/classes/qaction#setshortcut) + */ + shortcut?: QKeySequence; + + /** + * Sets the context for action's shortcut. [QAction: setShortcutContext](https://docs.nodegui.org/docs/api/generated/classes/qaction#setshortcutcontext) + */ + shortcutContext?: ShortcutContext; + + /** + * Sets descriptive text. [QAction: setText](https://docs.nodegui.org/docs/api/generated/classes/qaction#settext) + */ + text?: string; +} + +const setActionProps = ( + widget: RNAction, + newProps: ActionProps, + oldProps: ActionProps +) => { + const setter: ActionProps = { + set checkable(isCheckable: boolean) { + widget.setCheckable(isCheckable); + }, + set checked(isChecked: boolean) { + widget.setChecked(isChecked); + }, + set enabled(isEnabled: boolean) { + widget.setEnabled(isEnabled); + }, + set font(font: QFont) { + widget.setFont(font); + }, + set icon(icon: QIcon) { + widget.setIcon(icon); + }, + set id(id: string) { + widget.setObjectName(id); + }, + set on(listenerMap: Partial) { + const listenerMapLatest: any = Object.assign({}, listenerMap); + const oldListenerMap = Object.assign({}, oldProps.on); + Object.entries(oldListenerMap).forEach(([eventType, oldEvtListener]) => { + const newEvtListener = listenerMapLatest[eventType]; + if (oldEvtListener !== newEvtListener) { + widget.removeEventListener(eventType as any, oldEvtListener); + } else { + delete listenerMapLatest[eventType]; + } + }); + + Object.entries(listenerMapLatest).forEach( + ([eventType, newEvtListener]) => { + widget.addEventListener(eventType as any, newEvtListener); + } + ); + }, + set separator(isSeparator: boolean) { + widget.setSeparator(isSeparator); + }, + set shortcut(shortcut: QKeySequence) { + widget.setShortcut(shortcut); + }, + set shortcutContext(shortcutContext: ShortcutContext) { + widget.setShortcutContext(shortcutContext); + }, + set text(text: string) { + widget.setText(text); + }, + }; + Object.assign(setter, newProps); +}; + +export class RNAction extends QAction implements RNComponent { + setProps(newProps: ActionProps, oldProps: ActionProps): void { + setActionProps(this, newProps, oldProps); + } + appendInitialChild(child: Component) { + throwUnsupported(this); + } + appendChild(child: Component): void { + throwUnsupported(this); + } + insertBefore(child: Component, beforeChild: Component): void { + throwUnsupported(this); + } + removeChild(child: Component): void { + throwUnsupported(this); + } + static tagName = "action"; +} diff --git a/src/components/Action/index.ts b/src/components/Action/index.ts new file mode 100644 index 00000000..a4c86c9b --- /dev/null +++ b/src/components/Action/index.ts @@ -0,0 +1,37 @@ +import { Fiber } from "react-reconciler"; +import { registerComponent, ComponentConfig } from "../config"; +import { RNAction, ActionProps } from "./RNAction"; +import { AppContainer } from "../../reconciler"; + +class ActionConfig extends ComponentConfig { + tagName = RNAction.tagName; + shouldSetTextContent(nextProps: ActionProps): boolean { + return false; + } + createInstance( + newProps: ActionProps, + rootInstance: AppContainer, + context: any, + workInProgress: Fiber + ): RNAction { + const widget = new RNAction(); + widget.setProps(newProps, {}); + return widget; + } + commitMount( + instance: RNAction, + newProps: ActionProps, + internalInstanceHandle: any + ): void {} + commitUpdate( + instance: RNAction, + updatePayload: any, + oldProps: ActionProps, + newProps: ActionProps, + finishedWork: Fiber + ): void { + instance.setProps(newProps, oldProps); + } +} + +export const Action = registerComponent(new ActionConfig()); diff --git a/src/components/Menu/RNMenu.ts b/src/components/Menu/RNMenu.ts index f74bbc46..68656942 100644 --- a/src/components/Menu/RNMenu.ts +++ b/src/components/Menu/RNMenu.ts @@ -1,11 +1,11 @@ -import { NodeWidget, QAction, QMenu, QMenuSignals } from "@nodegui/nodegui"; -import { ViewProps, setViewProps } from "../View/RNView"; +import { QMenu, QMenuSignals, Component, NodeWidget } from "@nodegui/nodegui"; import { RNWidget } from "../config"; import { throwUnsupported } from "../../utils/helpers"; +import { RNAction } from "../Action/RNAction"; +import { setViewProps, ViewProps } from "../View/RNView"; export interface MenuProps extends ViewProps { title?: string; - actions?: QAction[]; } const setMenuProps = ( @@ -17,11 +17,6 @@ const setMenuProps = ( set title(title: string) { widget.setTitle(title); }, - set actions(actions: QAction[]) { - actions.forEach(action => { - widget.addAction(action); - }); - } }; Object.assign(setter, newProps); setViewProps(widget, newProps, oldProps); @@ -31,17 +26,24 @@ export class RNMenu extends QMenu implements RNWidget { setProps(newProps: MenuProps, oldProps: MenuProps): void { setMenuProps(this, newProps, oldProps); } - appendInitialChild(child: NodeWidget): void { - throwUnsupported(this); + appendInitialChild(child: Component): void { + this.appendChild(child); } - appendChild(child: NodeWidget): void { - throwUnsupported(this); + appendChild(child: Component): void { + if (!(child instanceof RNAction)) { + console.warn("Menu only supports Action as its children"); + return; + } + + this.addAction(child); } - insertBefore(child: NodeWidget, beforeChild: NodeWidget): void { + insertBefore(child: Component, beforeChild: Component): void { throwUnsupported(this); } - removeChild(child: NodeWidget): void { - throwUnsupported(this); + removeChild(child: Component): void { + if (child instanceof RNAction) { + this.removeAction(child); + } } static tagName = "menu"; -}; +} diff --git a/src/demo.tsx b/src/demo.tsx index 95af9c21..436234cb 100644 --- a/src/demo.tsx +++ b/src/demo.tsx @@ -1,42 +1,59 @@ import React from "react"; -import { QIcon, QAction, QApplication } from "@nodegui/nodegui"; +import { QIcon, QApplication, QKeySequence } from "@nodegui/nodegui"; import path from "path"; -import { MenuBar, Menu, SystemTrayIcon, Renderer, Window } from "."; - -const quitAction = new QAction(); -quitAction.setText("Quit"); -quitAction.addEventListener("triggered", () => { - const app = QApplication.instance(); - app.exit(0); -}); - -const fileActions: QAction[] = [quitAction]; - -const sayHi = new QAction(); -sayHi.setText("Hello"); -sayHi.addEventListener("triggered", () => { - console.log("hello"); -}); - -const randActions: QAction[] = [sayHi]; +import { Action, MenuBar, Menu, SystemTrayIcon, Renderer, Window } from "."; + +const quitAction = ( + { + QApplication.instance().exit(0); + }, + }} + shortcut={new QKeySequence("Ctrl+Q")} + text="Quit" + /> +); +const sayHiAction = ( + { + console.log("hello"); + }, + }} + text="Hello" + /> +); const trayIcon = new QIcon( path.join(__dirname, "../extras/assets/nodegui.png") ); -const separatorAction = new QAction(); -separatorAction.setSeparator(true); - -const systemTrayMenuActions = [sayHi, separatorAction, quitAction]; const App = () => { return ( - + + { + console.log("print"); + }, + }} + text="Print" + shortcut={new QKeySequence("Ctrl+P")} + /> + + {quitAction} + - - + + {sayHiAction} + + {sayHiAction} + {quitAction} + ); diff --git a/src/index.ts b/src/index.ts index 98679bb4..7ca7dec3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +export { Action } from "./components/Action"; export { BoxView } from "./components/BoxView"; export { GridView } from "./components/GridView"; export { Slider } from "./components/Slider";