Skip to content

Commit 21752ac

Browse files
authored
Add Action component (#240)
* Add Action component * Enable Menu to have Action as children
1 parent 956e47e commit 21752ac

File tree

5 files changed

+245
-42
lines changed

5 files changed

+245
-42
lines changed

src/components/Action/RNAction.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import {
2+
QAction,
3+
QIcon,
4+
QActionSignals,
5+
Component,
6+
QFont,
7+
QShortcut,
8+
QKeySequence,
9+
ShortcutContext,
10+
} from "@nodegui/nodegui";
11+
import { RNComponent, RNProps } from "../config";
12+
import { throwUnsupported } from "../../utils/helpers";
13+
14+
export interface ActionProps extends RNProps {
15+
/**
16+
* Sets whether the action is a checkable action. [QAction: setCheckable](https://docs.nodegui.org/docs/api/generated/classes/qaction#setcheckable)
17+
*/
18+
checkable?: boolean;
19+
20+
/**
21+
* Sets whether the action is checked. [QAction: setChecked](https://docs.nodegui.org/docs/api/generated/classes/qaction#setchecked)
22+
*/
23+
checked?: boolean;
24+
25+
/**
26+
* Sets whether the action is enabled. [QAction: setEnabled](https://docs.nodegui.org/docs/api/generated/classes/qaction#setenabled)
27+
*/
28+
enabled?: boolean;
29+
30+
/**
31+
* Sets a font for the action. [QAction: setFont](https://docs.nodegui.org/docs/api/generated/classes/qaction#setfont)
32+
*/
33+
font?: QFont;
34+
35+
/**
36+
* Sets an icon for the action. [QSystemTrayIcon: setIcon](https://docs.nodegui.org/docs/api/generated/classes/qsystemtrayicon#seticon)
37+
*/
38+
icon?: QIcon;
39+
40+
/**
41+
* 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)
42+
*/
43+
id?: string;
44+
45+
/**
46+
* Prop to set the event listener map. See [Handlong Events](/docs/guides/handle-events)
47+
*/
48+
on?: Partial<QActionSignals>;
49+
50+
/**
51+
* Sets whether this action will be considered a separator. [QAction: setSeparator](https://docs.nodegui.org/docs/api/generated/classes/qaction#setseparator)
52+
*/
53+
separator?: boolean;
54+
55+
/**
56+
* Sets the action's primary shortcut key. [QAction: setShortcut](https://docs.nodegui.org/docs/api/generated/classes/qaction#setshortcut)
57+
*/
58+
shortcut?: QKeySequence;
59+
60+
/**
61+
* Sets the context for action's shortcut. [QAction: setShortcutContext](https://docs.nodegui.org/docs/api/generated/classes/qaction#setshortcutcontext)
62+
*/
63+
shortcutContext?: ShortcutContext;
64+
65+
/**
66+
* Sets descriptive text. [QAction: setText](https://docs.nodegui.org/docs/api/generated/classes/qaction#settext)
67+
*/
68+
text?: string;
69+
}
70+
71+
const setActionProps = (
72+
widget: RNAction,
73+
newProps: ActionProps,
74+
oldProps: ActionProps
75+
) => {
76+
const setter: ActionProps = {
77+
set checkable(isCheckable: boolean) {
78+
widget.setCheckable(isCheckable);
79+
},
80+
set checked(isChecked: boolean) {
81+
widget.setChecked(isChecked);
82+
},
83+
set enabled(isEnabled: boolean) {
84+
widget.setEnabled(isEnabled);
85+
},
86+
set font(font: QFont) {
87+
widget.setFont(font);
88+
},
89+
set icon(icon: QIcon) {
90+
widget.setIcon(icon);
91+
},
92+
set id(id: string) {
93+
widget.setObjectName(id);
94+
},
95+
set on(listenerMap: Partial<QActionSignals>) {
96+
const listenerMapLatest: any = Object.assign({}, listenerMap);
97+
const oldListenerMap = Object.assign({}, oldProps.on);
98+
Object.entries(oldListenerMap).forEach(([eventType, oldEvtListener]) => {
99+
const newEvtListener = listenerMapLatest[eventType];
100+
if (oldEvtListener !== newEvtListener) {
101+
widget.removeEventListener(eventType as any, oldEvtListener);
102+
} else {
103+
delete listenerMapLatest[eventType];
104+
}
105+
});
106+
107+
Object.entries(listenerMapLatest).forEach(
108+
([eventType, newEvtListener]) => {
109+
widget.addEventListener(eventType as any, newEvtListener);
110+
}
111+
);
112+
},
113+
set separator(isSeparator: boolean) {
114+
widget.setSeparator(isSeparator);
115+
},
116+
set shortcut(shortcut: QKeySequence) {
117+
widget.setShortcut(shortcut);
118+
},
119+
set shortcutContext(shortcutContext: ShortcutContext) {
120+
widget.setShortcutContext(shortcutContext);
121+
},
122+
set text(text: string) {
123+
widget.setText(text);
124+
},
125+
};
126+
Object.assign(setter, newProps);
127+
};
128+
129+
export class RNAction extends QAction implements RNComponent {
130+
setProps(newProps: ActionProps, oldProps: ActionProps): void {
131+
setActionProps(this, newProps, oldProps);
132+
}
133+
appendInitialChild(child: Component) {
134+
throwUnsupported(this);
135+
}
136+
appendChild(child: Component): void {
137+
throwUnsupported(this);
138+
}
139+
insertBefore(child: Component, beforeChild: Component): void {
140+
throwUnsupported(this);
141+
}
142+
removeChild(child: Component): void {
143+
throwUnsupported(this);
144+
}
145+
static tagName = "action";
146+
}

src/components/Action/index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Fiber } from "react-reconciler";
2+
import { registerComponent, ComponentConfig } from "../config";
3+
import { RNAction, ActionProps } from "./RNAction";
4+
import { AppContainer } from "../../reconciler";
5+
6+
class ActionConfig extends ComponentConfig {
7+
tagName = RNAction.tagName;
8+
shouldSetTextContent(nextProps: ActionProps): boolean {
9+
return false;
10+
}
11+
createInstance(
12+
newProps: ActionProps,
13+
rootInstance: AppContainer,
14+
context: any,
15+
workInProgress: Fiber
16+
): RNAction {
17+
const widget = new RNAction();
18+
widget.setProps(newProps, {});
19+
return widget;
20+
}
21+
commitMount(
22+
instance: RNAction,
23+
newProps: ActionProps,
24+
internalInstanceHandle: any
25+
): void {}
26+
commitUpdate(
27+
instance: RNAction,
28+
updatePayload: any,
29+
oldProps: ActionProps,
30+
newProps: ActionProps,
31+
finishedWork: Fiber
32+
): void {
33+
instance.setProps(newProps, oldProps);
34+
}
35+
}
36+
37+
export const Action = registerComponent<ActionProps>(new ActionConfig());

src/components/Menu/RNMenu.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { NodeWidget, QAction, QMenu, QMenuSignals } from "@nodegui/nodegui";
2-
import { ViewProps, setViewProps } from "../View/RNView";
1+
import { QMenu, QMenuSignals, Component, NodeWidget } from "@nodegui/nodegui";
32
import { RNWidget } from "../config";
43
import { throwUnsupported } from "../../utils/helpers";
4+
import { RNAction } from "../Action/RNAction";
5+
import { setViewProps, ViewProps } from "../View/RNView";
56

67
export interface MenuProps extends ViewProps<QMenuSignals> {
78
title?: string;
8-
actions?: QAction[];
99
}
1010

1111
const setMenuProps = (
@@ -17,11 +17,6 @@ const setMenuProps = (
1717
set title(title: string) {
1818
widget.setTitle(title);
1919
},
20-
set actions(actions: QAction[]) {
21-
actions.forEach(action => {
22-
widget.addAction(action);
23-
});
24-
}
2520
};
2621
Object.assign(setter, newProps);
2722
setViewProps(widget, newProps, oldProps);
@@ -31,17 +26,24 @@ export class RNMenu extends QMenu implements RNWidget {
3126
setProps(newProps: MenuProps, oldProps: MenuProps): void {
3227
setMenuProps(this, newProps, oldProps);
3328
}
34-
appendInitialChild(child: NodeWidget<any>): void {
35-
throwUnsupported(this);
29+
appendInitialChild(child: Component): void {
30+
this.appendChild(child);
3631
}
37-
appendChild(child: NodeWidget<any>): void {
38-
throwUnsupported(this);
32+
appendChild(child: Component): void {
33+
if (!(child instanceof RNAction)) {
34+
console.warn("Menu only supports Action as its children");
35+
return;
36+
}
37+
38+
this.addAction(child);
3939
}
40-
insertBefore(child: NodeWidget<any>, beforeChild: NodeWidget<any>): void {
40+
insertBefore(child: Component, beforeChild: Component): void {
4141
throwUnsupported(this);
4242
}
43-
removeChild(child: NodeWidget<any>): void {
44-
throwUnsupported(this);
43+
removeChild(child: Component): void {
44+
if (child instanceof RNAction) {
45+
this.removeAction(child);
46+
}
4547
}
4648
static tagName = "menu";
47-
};
49+
}

src/demo.tsx

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,59 @@
11
import React from "react";
2-
import { QIcon, QAction, QApplication } from "@nodegui/nodegui";
2+
import { QIcon, QApplication, QKeySequence } from "@nodegui/nodegui";
33
import path from "path";
4-
import { MenuBar, Menu, SystemTrayIcon, Renderer, Window } from ".";
5-
6-
const quitAction = new QAction();
7-
quitAction.setText("Quit");
8-
quitAction.addEventListener("triggered", () => {
9-
const app = QApplication.instance();
10-
app.exit(0);
11-
});
12-
13-
const fileActions: QAction[] = [quitAction];
14-
15-
const sayHi = new QAction();
16-
sayHi.setText("Hello");
17-
sayHi.addEventListener("triggered", () => {
18-
console.log("hello");
19-
});
20-
21-
const randActions: QAction[] = [sayHi];
4+
import { Action, MenuBar, Menu, SystemTrayIcon, Renderer, Window } from ".";
5+
6+
const quitAction = (
7+
<Action
8+
on={{
9+
triggered: () => {
10+
QApplication.instance().exit(0);
11+
},
12+
}}
13+
shortcut={new QKeySequence("Ctrl+Q")}
14+
text="Quit"
15+
/>
16+
);
17+
const sayHiAction = (
18+
<Action
19+
on={{
20+
triggered: () => {
21+
console.log("hello");
22+
},
23+
}}
24+
text="Hello"
25+
/>
26+
);
2227

2328
const trayIcon = new QIcon(
2429
path.join(__dirname, "../extras/assets/nodegui.png")
2530
);
26-
const separatorAction = new QAction();
27-
separatorAction.setSeparator(true);
28-
29-
const systemTrayMenuActions = [sayHi, separatorAction, quitAction];
3031

3132
const App = () => {
3233
return (
3334
<Window>
3435
<SystemTrayIcon icon={trayIcon} tooltip="React Nodegui" visible>
35-
<Menu actions={systemTrayMenuActions} />
36+
<Menu>
37+
<Action
38+
on={{
39+
triggered: () => {
40+
console.log("print");
41+
},
42+
}}
43+
text="Print"
44+
shortcut={new QKeySequence("Ctrl+P")}
45+
/>
46+
<Action separator />
47+
{quitAction}
48+
</Menu>
3649
</SystemTrayIcon>
3750
<MenuBar>
38-
<Menu title={"File"} actions={fileActions} />
39-
<Menu title={"Random"} actions={randActions} />
51+
<Menu title="Random">
52+
{sayHiAction}
53+
<Action separator />
54+
{sayHiAction}
55+
{quitAction}
56+
</Menu>
4057
</MenuBar>
4158
</Window>
4259
);

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { Action } from "./components/Action";
12
export { BoxView } from "./components/BoxView";
23
export { GridView } from "./components/GridView";
34
export { Slider } from "./components/Slider";

0 commit comments

Comments
 (0)