├── .gitignore ├── README.md ├── nodegui ├── .gitkeep ├── calculator │ ├── .gitignore │ ├── README.md │ ├── calculator_linux.png │ ├── calculator_mac.png │ ├── calculator_win.jpg │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js ├── clipboard-image │ ├── .gitignore │ ├── README.md │ ├── assets.d.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js ├── custom-native-widget-qpainter │ ├── .gitignore │ ├── README.md │ ├── assets │ │ └── analog-clock.gif │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js ├── password-generator │ └── README.md └── systray │ ├── .gitignore │ ├── README.md │ ├── assets │ ├── nodegui_white.png │ └── systray-example.gif │ ├── package-lock.json │ ├── package.json │ ├── src │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js └── react-nodegui ├── SystemUtility └── README.md ├── calculator ├── README.md ├── calculator_linux.png ├── calculator_mac.png ├── calculator_win.jpg ├── index.tsx ├── package-lock.json ├── package.json └── tsconfig.json ├── image-view ├── README.md ├── image_view_linux.png ├── image_view_mac.png ├── image_view_win.jpg ├── index.tsx ├── package-lock.json ├── package.json └── tsconfig.json ├── react-router-example ├── .babelrc ├── .gitignore ├── README.md ├── assets.d.ts ├── assets │ └── routes.gif ├── package-lock.json ├── package.json ├── src │ ├── app.tsx │ ├── index.tsx │ ├── pages │ │ ├── About.tsx │ │ └── Home.tsx │ └── routes.tsx ├── tsconfig.json └── webpack.config.js └── weather-app-widget ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── assets │ └── icons │ │ ├── 01d.png │ │ ├── 01n.png │ │ ├── 02d.png │ │ ├── 02n.png │ │ ├── 03d.png │ │ ├── 03n.png │ │ ├── 04d.png │ │ ├── 04n.png │ │ ├── 09d.png │ │ ├── 09n.png │ │ ├── 10d.png │ │ ├── 10n.png │ │ ├── 11d.png │ │ ├── 11n.png │ │ ├── 13d.png │ │ ├── 13n.png │ │ ├── 50d.png │ │ ├── 50n.png │ │ └── na.png ├── components │ ├── PlaceDate.tsx │ ├── Summary.tsx │ ├── TemperatureBox.tsx │ └── WeatherIcon.tsx ├── index.tsx └── utils │ ├── helpers.ts │ └── weather.ts ├── tsconfig.json └── weather_widget_mac.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | Repo containing example apps made with NodeGUI and React NodeGUI 4 | -------------------------------------------------------------------------------- /nodegui/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/.gitkeep -------------------------------------------------------------------------------- /nodegui/calculator/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.log 4 | .vscode -------------------------------------------------------------------------------- /nodegui/calculator/README.md: -------------------------------------------------------------------------------- 1 | # Calculator app 2 | 3 | This example showcases how to build a basic calculator clone. 4 | 5 | ### Screenshots 6 | 7 | **Linux** 8 | 9 | demo_linux 10 | 11 | **Windows** 12 | 13 | demo_win 14 | 15 | **Mac:** 16 | 17 | demo_mac 18 | 19 | To run the demo: 20 | 21 | 0. `cd calculator` 22 | 23 | 1. `npm install` 24 | 25 | 1. `npm start` 26 | -------------------------------------------------------------------------------- /nodegui/calculator/calculator_linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/calculator/calculator_linux.png -------------------------------------------------------------------------------- /nodegui/calculator/calculator_mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/calculator/calculator_mac.png -------------------------------------------------------------------------------- /nodegui/calculator/calculator_win.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/calculator/calculator_win.jpg -------------------------------------------------------------------------------- /nodegui/calculator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calculator", 3 | "version": "1.0.0", 4 | "description": "This example showcases how to build a basic calculator clone.", 5 | "main": "index.js", 6 | "author": "Atul R ", 7 | "license": "MIT", 8 | "private": true, 9 | "scripts": { 10 | "build": "webpack -p", 11 | "start": "webpack && qode ./dist/index.js", 12 | "debug": "webpack && qode --inspect ./dist/index.js" 13 | }, 14 | "dependencies": { 15 | "@nodegui/nodegui": "^0.6.5" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^12.12.8", 19 | "file-loader": "^4.2.0", 20 | "node-loader": "^0.6.0", 21 | "ts-loader": "^6.2.1", 22 | "typescript": "^3.7.2", 23 | "webpack": "^4.41.2", 24 | "webpack-cli": "^3.3.10" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nodegui/calculator/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | QMainWindow, 3 | QPushButton, 4 | QPushButtonEvents, 5 | QWidget, 6 | QKeyEvent, 7 | FlexLayout, 8 | QLabel, 9 | BaseWidgetEvents, 10 | NativeElement 11 | } from "@nodegui/nodegui"; 12 | 13 | // =============== 14 | // UI AND DESIGN 15 | // =============== 16 | const getButton = ( 17 | label: string, 18 | value: number | string, 19 | type: "value" | "command" 20 | ) => { 21 | const button = new QPushButton(); 22 | button.setText(label); 23 | button.setObjectName(`btn${value}`); 24 | button.addEventListener(QPushButtonEvents.clicked, () => { 25 | onBtnClick(value, type); 26 | }); 27 | return { 28 | ui: button, 29 | value, 30 | type 31 | }; 32 | }; 33 | 34 | // Main Window 35 | const win = new QMainWindow(); 36 | win.setFixedSize(230, 300); 37 | 38 | // Root view 39 | const rootView = new QWidget(); 40 | win.addEventListener( 41 | BaseWidgetEvents.KeyRelease, 42 | (nativeEvent: NativeElement) => { 43 | const keyEvt = new QKeyEvent(nativeEvent); 44 | const text = keyEvt.text(); 45 | const isNotNumber = isNaN(parseInt(text)); 46 | onBtnClick(text, isNotNumber ? "command" : "value"); 47 | } 48 | ); 49 | rootView.setObjectName("rootView"); //This is like ids in web world 50 | win.setCentralWidget(rootView); 51 | const rootStyleSheet = ` 52 | * { 53 | font-size: 20px; 54 | color: white; 55 | } 56 | 57 | QPushButton { 58 | min-width: '25%'; 59 | height: 60px; 60 | border: 1px solid black; 61 | } 62 | 63 | QPushButton:pressed { 64 | background: grey; 65 | } 66 | 67 | #rootView { 68 | flex: 1; 69 | flex-direction: column; 70 | } 71 | 72 | #btnAC { 73 | min-width: '25%'; 74 | border-right: 2px solid black; 75 | } 76 | 77 | #resultText { 78 | flex: 1; 79 | qproperty-alignment: 'AlignRight|AlignVCenter'; 80 | padding-right: 5px; 81 | font-size: 40px; 82 | } 83 | 84 | #row0,#row1,#row2,#row3,#row4 { 85 | flex: 1; 86 | justify-content: space-between; 87 | flex-direction: row; 88 | } 89 | 90 | #row0 * { 91 | background: #1E1E1E; 92 | } 93 | 94 | #row0 QPushButton:pressed { 95 | background: grey; 96 | } 97 | 98 | #row1 QPushButton { 99 | background: #2E2E2E; 100 | } 101 | 102 | #row1 QPushButton:pressed { 103 | background: grey; 104 | } 105 | 106 | 107 | #row2 QPushButton, #row2 QPushButton, #row3 QPushButton, #row4 QPushButton { 108 | background: #4B4B4B; 109 | } 110 | 111 | #row2 QPushButton:pressed, #row2 QPushButton:pressed, #row3 QPushButton:pressed, #row4 QPushButton:pressed { 112 | background: grey; 113 | } 114 | `; 115 | 116 | const operatorStyleSheet = ` 117 | QPushButton { 118 | background: #FD8D0E; 119 | } 120 | 121 | QPushButton:pressed { 122 | background: grey; 123 | } 124 | `; 125 | 126 | rootView.setStyleSheet(rootStyleSheet); 127 | const rootViewFlexLayout = new FlexLayout(); 128 | rootViewFlexLayout.setFlexNode(rootView.getFlexNode()); 129 | rootView.setLayout(rootViewFlexLayout); 130 | 131 | // Top Row 132 | const btnClear = getButton("AC", "AC", "command"); 133 | const resultText = new QLabel(); 134 | resultText.setObjectName("resultText"); 135 | resultText.setText(0); 136 | const row0 = new QWidget(); 137 | row0.setObjectName("row0"); 138 | 139 | const row0Layout = new FlexLayout(); 140 | row0Layout.setFlexNode(row0.getFlexNode()); 141 | row0.setLayout(row0Layout); 142 | row0Layout.addWidget(btnClear.ui, btnClear.ui.getFlexNode()); 143 | row0Layout.addWidget(resultText, resultText.getFlexNode()); 144 | 145 | // First Row 146 | const btn7 = getButton("7", 7, "value"); 147 | const btn8 = getButton("8", 8, "value"); 148 | const btn9 = getButton("9", 9, "value"); 149 | const btnDivide = getButton("/", "/", "command"); 150 | btnDivide.ui.setStyleSheet(operatorStyleSheet); 151 | const row1 = new QWidget(); 152 | row1.setObjectName("row1"); 153 | const row1Layout = new FlexLayout(); 154 | row1Layout.setFlexNode(row1.getFlexNode()); 155 | row1Layout.addWidget(btn7.ui, btn7.ui.getFlexNode()); 156 | row1Layout.addWidget(btn8.ui, btn8.ui.getFlexNode()); 157 | row1Layout.addWidget(btn9.ui, btn9.ui.getFlexNode()); 158 | row1Layout.addWidget(btnDivide.ui, btnDivide.ui.getFlexNode()); 159 | row1.setLayout(row1Layout); 160 | 161 | // Second Row 162 | const btn4 = getButton("4", 4, "value"); 163 | const btn5 = getButton("5", 5, "value"); 164 | const btn6 = getButton("6", 6, "value"); 165 | const btnMultiply = getButton("x", "*", "command"); 166 | btnMultiply.ui.setStyleSheet(operatorStyleSheet); 167 | const row2 = new QWidget(); 168 | row2.setObjectName("row2"); 169 | const row2Layout = new FlexLayout(); 170 | row2Layout.setFlexNode(row2.getFlexNode()); 171 | row2Layout.addWidget(btn4.ui, btn4.ui.getFlexNode()); 172 | row2Layout.addWidget(btn5.ui, btn5.ui.getFlexNode()); 173 | row2Layout.addWidget(btn6.ui, btn6.ui.getFlexNode()); 174 | row2Layout.addWidget(btnMultiply.ui, btnMultiply.ui.getFlexNode()); 175 | row2.setLayout(row2Layout); 176 | 177 | // Third Row 178 | const btn1 = getButton("1", 1, "value"); 179 | const btn2 = getButton("2", 2, "value"); 180 | const btn3 = getButton("3", 3, "value"); 181 | const btnMinus = getButton("-", "-", "command"); 182 | btnMinus.ui.setStyleSheet(operatorStyleSheet); 183 | 184 | const row3 = new QWidget(); 185 | row3.setObjectName("row3"); 186 | 187 | const row3Layout = new FlexLayout(); 188 | row3Layout.setFlexNode(row3.getFlexNode()); 189 | row3Layout.addWidget(btn1.ui, btn1.ui.getFlexNode()); 190 | row3Layout.addWidget(btn2.ui, btn2.ui.getFlexNode()); 191 | row3Layout.addWidget(btn3.ui, btn3.ui.getFlexNode()); 192 | row3Layout.addWidget(btnMinus.ui, btnMinus.ui.getFlexNode()); 193 | row3.setLayout(row3Layout); 194 | 195 | // Fourth Row 196 | const btn0 = getButton("0", 0, "value"); 197 | const btnDot = getButton(".", ".", "command"); 198 | const btnEquals = getButton("=", "=", "command"); 199 | const btnPlus = getButton("+", "+", "command"); 200 | btnPlus.ui.setStyleSheet(operatorStyleSheet); 201 | 202 | const row4 = new QWidget(); 203 | row4.setObjectName("row4"); 204 | const row4Layout = new FlexLayout(); 205 | row4Layout.setFlexNode(row4.getFlexNode()); 206 | row4Layout.addWidget(btn0.ui, btn0.ui.getFlexNode()); 207 | row4Layout.addWidget(btnDot.ui, btnDot.ui.getFlexNode()); 208 | row4Layout.addWidget(btnEquals.ui, btnEquals.ui.getFlexNode()); 209 | row4Layout.addWidget(btnPlus.ui, btnPlus.ui.getFlexNode()); 210 | row4.setLayout(row4Layout); 211 | 212 | // Add to root view 213 | rootViewFlexLayout.addWidget(row0, row0.getFlexNode()); 214 | rootViewFlexLayout.addWidget(row1, row1.getFlexNode()); 215 | rootViewFlexLayout.addWidget(row2, row2.getFlexNode()); 216 | rootViewFlexLayout.addWidget(row3, row3.getFlexNode()); 217 | rootViewFlexLayout.addWidget(row4, row4.getFlexNode()); 218 | 219 | win.show(); 220 | 221 | (global as any).win = win; //to keep gc from collecting ui widgets 222 | 223 | // ======================== 224 | // CALC APP LOGIC - LOGIC 225 | // ======================== 226 | // This is probably the worst calculator logic ever but the purpose of demo is to showcase the UI and not the js logic. 227 | // Read ahead of this line at your own risk. 228 | 229 | let displayText = "0"; 230 | let currentInputString = ""; 231 | let total = 0; 232 | let previousOperator = "+"; 233 | 234 | var onBtnClick = (value: number | string, type: "value" | "command") => { 235 | if ( 236 | ![ 237 | "0", 238 | "1", 239 | "2", 240 | "3", 241 | "4", 242 | "5", 243 | "6", 244 | "7", 245 | "8", 246 | "9", 247 | "+", 248 | "-", 249 | "/", 250 | "*", 251 | "=", 252 | "AC" 253 | ].includes(`${value}`) 254 | ) { 255 | return; 256 | } 257 | if (type === "value" || value === ".") { 258 | currentInputString += value; 259 | displayText = currentInputString; 260 | } else { 261 | const currentInput = parseFloat(currentInputString || "0"); 262 | if (!previousOperator) { 263 | if (currentInputString) { 264 | total = currentInput; 265 | } 266 | } 267 | if (!currentInputString && value === "=") { 268 | previousOperator = "+"; 269 | } 270 | switch (previousOperator) { 271 | case "+": { 272 | total += currentInput; 273 | break; 274 | } 275 | case "-": { 276 | total -= currentInput; 277 | break; 278 | } 279 | case "*": { 280 | total *= currentInput; 281 | break; 282 | } 283 | case "/": { 284 | total /= currentInput; 285 | break; 286 | } 287 | } 288 | currentInputString = ""; 289 | 290 | if (value === "=") { 291 | displayText = String(total); 292 | previousOperator = ""; 293 | } else { 294 | previousOperator = String(value); 295 | displayText = previousOperator; 296 | } 297 | } 298 | 299 | if (value === "AC") { 300 | displayText = "0"; 301 | currentInputString = ""; 302 | total = 0; 303 | previousOperator = "+"; 304 | } 305 | 306 | if (Number.isNaN(total)) { 307 | total = 0; 308 | displayText = "Error"; 309 | } 310 | 311 | // SET THE FINAL TEXT 312 | resultText.setText(displayText); 313 | }; 314 | -------------------------------------------------------------------------------- /nodegui/calculator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2016", 5 | "module": "commonjs", 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "./dist", 9 | "sourceMap": true, 10 | "strict": true, 11 | "alwaysStrict": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true 14 | }, 15 | "include": ["**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /nodegui/calculator/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | mode: process.NODE_ENV || "development", 5 | entry: "./src", 6 | target: "node", 7 | output: { 8 | path: path.resolve(__dirname, "dist"), 9 | filename: "index.js" 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | use: "ts-loader", 16 | exclude: /node_modules/ 17 | }, 18 | { 19 | test: /\.(png|jpe?g|gif|svg)$/i, 20 | use: [{ loader: "file-loader" }] 21 | }, 22 | { 23 | test: /\.node/i, 24 | use: [ 25 | { loader: "node-loader" }, 26 | { 27 | loader: "file-loader", 28 | options: { 29 | name: "[name].[ext]" 30 | } 31 | } 32 | ] 33 | } 34 | ] 35 | }, 36 | resolve: { 37 | extensions: [".tsx", ".ts", ".js"] 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.log 4 | .vscode -------------------------------------------------------------------------------- /nodegui/clipboard-image/README.md: -------------------------------------------------------------------------------- 1 | # Clipboard image reading and writing app 2 | 3 | This example showcases how to build a nodegui app that can read and write images to clipboard. 4 | 5 | To run the demo: 6 | 7 | 0. `cd clipboard-image` 8 | 9 | 1. `npm install` 10 | 11 | 1. `npm start` 12 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/assets.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg"; 2 | declare module "*.png"; 3 | declare module "*.jpg"; 4 | declare module "*.jpeg"; 5 | declare module "*.gif"; 6 | declare module "*.bmp"; 7 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodegui-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Atul R ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "build": "webpack -p", 10 | "start": "webpack && qode ./dist/index.js", 11 | "debug": "webpack && qode --inspect ./dist/index.js" 12 | }, 13 | "dependencies": { 14 | "@nodegui/nodegui": "^0.16.0" 15 | }, 16 | "devDependencies": { 17 | "@nodegui/packer": "^1.4.0", 18 | "@types/node": "^13.7.7", 19 | "clean-webpack-plugin": "^3.0.0", 20 | "file-loader": "^5.1.0", 21 | "native-addon-loader": "^2.0.1", 22 | "ts-loader": "^6.2.1", 23 | "typescript": "^3.8.3", 24 | "webpack": "^4.42.0", 25 | "webpack-cli": "^3.3.11" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | QMainWindow, 3 | QWidget, 4 | QPushButton, 5 | QLineEdit, 6 | FlexLayout, 7 | QApplication, 8 | QClipboardMode, 9 | QLabel, 10 | QPixmap 11 | } from "@nodegui/nodegui"; 12 | 13 | const win = new QMainWindow(); 14 | const center = new QWidget(); 15 | const label = new QLabel(); 16 | const textInput = new QLineEdit(); 17 | const getBtn = new QPushButton(); 18 | const setBtn = new QPushButton(); 19 | 20 | //---------- 21 | label.setText( 22 | "Copy any image onto the clipboard and click `Get clipbard image button`" 23 | ); 24 | getBtn.setText("Get clipboard image"); 25 | getBtn.addEventListener("clicked", () => { 26 | const clip = QApplication.clipboard(); 27 | const pixmap = clip.pixmap(QClipboardMode.Clipboard); 28 | label.setPixmap(pixmap); 29 | }); 30 | 31 | //-------------- 32 | textInput.setPlaceholderText( 33 | "Enter absolute image path to load into clipboard" 34 | ); 35 | setBtn.setText("Set clipboard image"); 36 | setBtn.addEventListener("clicked", () => { 37 | const clip = QApplication.clipboard(); 38 | const pixmap = new QPixmap(); 39 | pixmap.load(textInput.text()); 40 | clip.setPixmap(pixmap, QClipboardMode.Clipboard); 41 | label.setText(`Loaded image at ${textInput.text()} to global clipboard`); 42 | }); 43 | 44 | center.setLayout(new FlexLayout()); 45 | center.layout?.addWidget(textInput); 46 | center.layout?.addWidget(setBtn); 47 | center.layout?.addWidget(getBtn); 48 | center.layout?.addWidget(label); 49 | center.setInlineStyle(`width: 400; height: 400;`); 50 | win.setCentralWidget(center); 51 | win.show(); 52 | win.setFixedSize(400, 400); 53 | (global as any).win = win; 54 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2016", 5 | "module": "commonjs", 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "./dist", 9 | "sourceMap": true, 10 | "strict": true, 11 | "alwaysStrict": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true 14 | }, 15 | "include": ["**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /nodegui/clipboard-image/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 3 | 4 | module.exports = { 5 | mode: process.NODE_ENV || "development", 6 | entry: "./src", 7 | target: "node", 8 | output: { 9 | path: path.resolve(__dirname, "dist"), 10 | filename: "index.js" 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | use: "ts-loader", 17 | exclude: /node_modules/ 18 | }, 19 | { 20 | test: /\.(png|jpe?g|gif|svg)$/i, 21 | use: [ 22 | { 23 | loader: "file-loader", 24 | options: { publicPath: "dist" } 25 | } 26 | ] 27 | }, 28 | { 29 | test: /\.node$/, 30 | use: [ 31 | { 32 | loader: "native-addon-loader", 33 | options: { name: "[name]-[hash].[ext]" } 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | resolve: { 40 | extensions: [".tsx", ".ts", ".js", ".jsx"] 41 | }, 42 | plugins: [new CleanWebpackPlugin()] 43 | }; 44 | -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.log 4 | .vscode -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/README.md: -------------------------------------------------------------------------------- 1 | # Custom Native Widget built using QPainter from NodeGui 2 | 3 | An example of powerful QPainter from Qt in NodeGui that you can use to build completely custom native widgets using Javascript. 4 | The example showcases how to build a widget that looks and behaves like an analog clock. 5 | 6 | **PS: Keep in mind the whole clock in just one single widget** 7 | 8 | ### Screenshots 9 | 10 | demo_mac 11 | 12 | ## To Use 13 | 14 | To clone and run this repository you'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which comes with [npm](http://npmjs.com)) installed on your computer. 15 | 16 | Make sure you have met the requirements listed here: https://docs.nodegui.org/#/tutorial/development-environment 17 | 18 | From your command line: 19 | 20 | ```bash 21 | # Install dependencies 22 | npm install 23 | # Run the app 24 | npm start 25 | ``` 26 | 27 | ## Resources for Learning NodeGUI 28 | 29 | - [docs.nodegui.org](https://nodegui.github.io/nodegui) - all of NodeGui and React Desktop's documentation 30 | 31 | ## Extra info 32 | 33 | This is a javascript version of QPainter example using NodeGui from here: https://doc.qt.io/qt-5/qtgui-analogclock-example.html 34 | 35 | ## License 36 | 37 | MIT 38 | -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/assets/analog-clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/custom-native-widget-qpainter/assets/analog-clock.gif -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodegui-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Atul R ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "build": "webpack -p", 10 | "start": "webpack && qode ./dist/index.js", 11 | "debug": "webpack && qode --inspect ./dist/index.js" 12 | }, 13 | "dependencies": { 14 | "@nodegui/nodegui": "^0.13.0" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^12.12.17", 18 | "file-loader": "^5.0.2", 19 | "node-loader": "^0.6.0", 20 | "ts-loader": "^6.2.1", 21 | "typescript": "^3.7.3", 22 | "webpack": "^4.41.3", 23 | "webpack-cli": "^3.3.10" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FlexLayout, 3 | PenStyle, 4 | QColor, 5 | QMainWindow, 6 | QPainter, 7 | QPoint, 8 | QWidget, 9 | RenderHint, 10 | WidgetEventTypes 11 | } from "@nodegui/nodegui"; 12 | 13 | const win = new QMainWindow(); 14 | const center = new QWidget(); 15 | const layout = new FlexLayout(); 16 | const hourHand = [new QPoint(7, 8), new QPoint(-7, 8), new QPoint(0, -40)]; 17 | const minuteHand = [new QPoint(7, 8), new QPoint(-7, 8), new QPoint(0, -70)]; 18 | const secondHand = [new QPoint(4, 8), new QPoint(-4, 8), new QPoint(0, -70)]; 19 | const hourColor = new QColor(127, 0, 127); 20 | const minuteColor = new QColor(0, 127, 127, 191); 21 | const secondColor = new QColor(0, 0, 0); 22 | 23 | center.setLayout(layout); 24 | win.setWindowTitle("Analog Clock"); 25 | win.resize(200, 200); 26 | const side = Math.min(win.geometry().width(), win.geometry().height()); 27 | 28 | function repaint(): void { 29 | win.repaint(); 30 | setTimeout(repaint, 1000); 31 | } 32 | 33 | setTimeout(repaint, 1000); 34 | win.addEventListener(WidgetEventTypes.Paint, () => { 35 | const time = new Date(); 36 | 37 | const painter = new QPainter(win); 38 | painter.setRenderHint(RenderHint.Antialiasing); 39 | painter.translate(win.geometry().width() / 2, win.geometry().height() / 2); 40 | painter.scale(side / 200.0, side / 200.0); 41 | 42 | painter.setPen(PenStyle.NoPen); 43 | painter.setBrush(hourColor); 44 | 45 | painter.save(); 46 | painter.rotate(30.0 * (time.getHours() + time.getMinutes() / 60.0)); 47 | painter.drawConvexPolygon(hourHand); 48 | painter.restore(); 49 | 50 | painter.setPen(hourColor); 51 | 52 | for (let i = 0; i < 12; ++i) { 53 | painter.drawLine(88, 0, 96, 0); 54 | painter.rotate(30.0); 55 | } 56 | 57 | painter.setPen(PenStyle.NoPen); 58 | painter.setBrush(minuteColor); 59 | 60 | painter.save(); 61 | painter.rotate(6.0 * (time.getMinutes() + time.getSeconds() / 60.0)); 62 | painter.drawConvexPolygon(minuteHand); 63 | painter.restore(); 64 | 65 | painter.setBrush(secondColor); 66 | painter.setPen(PenStyle.NoPen); 67 | 68 | painter.save(); 69 | painter.rotate(360 * (time.getSeconds() / 60.0)); 70 | painter.drawConvexPolygon(secondHand); 71 | painter.restore(); 72 | 73 | painter.setPen(minuteColor); 74 | for (let j = 0; j < 60; ++j) { 75 | if (j % 5 != 0) { 76 | painter.drawLine(92, 0, 96, 0); 77 | } 78 | painter.rotate(6.0); 79 | } 80 | painter.end(); 81 | }); 82 | win.show(); 83 | (global as any).win = win; 84 | -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2016", 5 | "module": "commonjs", 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "./dist", 9 | "sourceMap": true, 10 | "strict": true, 11 | "alwaysStrict": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true 14 | }, 15 | "include": ["**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /nodegui/custom-native-widget-qpainter/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | mode: process.NODE_ENV || "development", 5 | entry: "./src", 6 | target: "node", 7 | output: { 8 | path: path.resolve(__dirname, "dist"), 9 | filename: "index.js" 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | use: "ts-loader", 16 | exclude: /node_modules/ 17 | }, 18 | { 19 | test: /\.(png|jpe?g|gif|svg)$/i, 20 | use: [{ loader: "file-loader" }] 21 | }, 22 | { 23 | test: /\.node/i, 24 | use: [ 25 | { loader: "node-loader" }, 26 | { 27 | loader: "file-loader", 28 | options: { 29 | name: "[name].[ext]" 30 | } 31 | } 32 | ] 33 | } 34 | ] 35 | }, 36 | resolve: { 37 | extensions: [".tsx", ".ts", ".js"] 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /nodegui/password-generator/README.md: -------------------------------------------------------------------------------- 1 | # Password generator 2 | 3 | A password generator app built with NodeGUI by [James Hibbard](https://github.com/jameshibbard) 4 | 5 | Link: https://github.com/jameshibbard/nodegui-password-generator 6 | 7 | 8 | ![Screenshot of app](https://res.cloudinary.com/hibbard/image/upload/v1568030141/node-gui/password-generator.png) 9 | -------------------------------------------------------------------------------- /nodegui/systray/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.log 4 | .vscode -------------------------------------------------------------------------------- /nodegui/systray/README.md: -------------------------------------------------------------------------------- 1 | # Systray app 2 | 3 | This example showcases how to build a nodegui app that runs on systray. Also, shows how to show/hide a window from systray. 4 | 5 | ### Screenshots 6 | 7 | demo_mac 8 | 9 | To run the demo: 10 | 11 | 0. `cd systray` 12 | 13 | 1. `npm install` 14 | 15 | 1. `npm start` 16 | -------------------------------------------------------------------------------- /nodegui/systray/assets/nodegui_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/systray/assets/nodegui_white.png -------------------------------------------------------------------------------- /nodegui/systray/assets/systray-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/nodegui/systray/assets/systray-example.gif -------------------------------------------------------------------------------- /nodegui/systray/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodegui-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Atul R ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "build": "webpack -p", 10 | "start": "webpack && qode ./dist/index.js", 11 | "debug": "webpack && qode --inspect ./dist/index.js" 12 | }, 13 | "dependencies": { 14 | "@nodegui/nodegui": "^0.15.3", 15 | "@nodegui/os-utils": "^1.1.2" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^13.9.1", 19 | "file-loader": "^4.2.0", 20 | "node-loader": "^0.6.0", 21 | "ts-loader": "^6.2.1", 22 | "typescript": "^3.8.3", 23 | "webpack": "^4.42.0", 24 | "webpack-cli": "^3.3.11" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nodegui/systray/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | QKeySequence, 3 | QApplication, 4 | QMainWindow, 5 | QMenu, 6 | QIcon, 7 | QSystemTrayIcon, 8 | QAction 9 | } from "@nodegui/nodegui"; 10 | import path from "path"; 11 | import { Dock } from "@nodegui/os-utils"; 12 | const icon = require("../assets/nodegui_white.png"); 13 | 14 | const win = new QMainWindow(); 15 | const trayIcon = new QIcon(path.resolve(__dirname, icon)); 16 | const tray = new QSystemTrayIcon(); 17 | tray.setIcon(trayIcon); 18 | tray.show(); 19 | tray.setToolTip("hello"); 20 | 21 | const menu = new QMenu(); 22 | tray.setContextMenu(menu); 23 | 24 | // ------------------- 25 | // Quit Action 26 | // ------------------- 27 | const quitAction = new QAction(); 28 | quitAction.setText("Quit"); 29 | quitAction.setIcon(trayIcon); 30 | quitAction.addEventListener("triggered", () => { 31 | const app = QApplication.instance(); 32 | app.exit(0); 33 | }); 34 | 35 | // ------------------- 36 | // Action with Submenu 37 | // ------------------- 38 | const actionWithSubmenu = new QAction(); 39 | const subMenu = new QMenu(); 40 | const hideDockAction = new QAction(); 41 | hideDockAction.setText("hide"); 42 | hideDockAction.addEventListener("triggered", () => { 43 | Dock.hide(); 44 | }); 45 | //----- 46 | const showDockAction = new QAction(); 47 | showDockAction.setText("show"); 48 | showDockAction.addEventListener("triggered", () => { 49 | Dock.show(); 50 | }); 51 | //----- 52 | subMenu.addAction(hideDockAction); 53 | subMenu.addAction(showDockAction); 54 | actionWithSubmenu.setMenu(subMenu); 55 | actionWithSubmenu.setText("Mac Dock"); 56 | 57 | // ---------------- 58 | // Dock Hide/Show 59 | // ---------------- 60 | const hideAction = new QAction(); 61 | hideAction.setText("hide window"); 62 | hideAction.setShortcut(new QKeySequence("Alt+H")); 63 | hideAction.addEventListener("triggered", () => { 64 | win.hide(); 65 | }); 66 | //----- 67 | const showAction = new QAction(); 68 | showAction.setText("show window"); 69 | showAction.setShortcut(new QKeySequence("Alt+S")); 70 | showAction.addEventListener("triggered", () => { 71 | win.show(); 72 | }); 73 | 74 | // ---------------------- 75 | // Add everything to menu 76 | // ---------------------- 77 | menu.addAction(hideAction); 78 | menu.addAction(showAction); 79 | menu.addAction(actionWithSubmenu); 80 | menu.addAction(quitAction); 81 | 82 | win.setWindowTitle("NodeGUI Demo"); 83 | win.resize(400, 700); 84 | win.show(); 85 | 86 | const qApp = QApplication.instance(); 87 | qApp.setQuitOnLastWindowClosed(false); // required so that app doesnt close if we close all windows. 88 | 89 | (global as any).win = win; // To prevent win from being garbage collected. 90 | (global as any).systemTray = tray; // To prevent system tray from being garbage collected. 91 | -------------------------------------------------------------------------------- /nodegui/systray/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2016", 5 | "module": "commonjs", 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "./dist", 9 | "sourceMap": true, 10 | "strict": true, 11 | "alwaysStrict": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true 14 | }, 15 | "include": ["**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /nodegui/systray/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | mode: process.NODE_ENV || "development", 5 | entry: "./src", 6 | target: "node", 7 | output: { 8 | path: path.resolve(__dirname, "dist"), 9 | filename: "index.js" 10 | }, 11 | node: { 12 | __dirname: false 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.tsx?$/, 18 | use: "ts-loader", 19 | exclude: /node_modules/ 20 | }, 21 | { 22 | test: /\.(png|jpe?g|gif|svg)$/i, 23 | use: [{ loader: "file-loader" }] 24 | }, 25 | { 26 | test: /\.node/i, 27 | use: [ 28 | { loader: "node-loader" }, 29 | { 30 | loader: "file-loader", 31 | options: { 32 | name: "[name].[ext]" 33 | } 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | resolve: { 40 | extensions: [".tsx", ".ts", ".js"] 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /react-nodegui/SystemUtility/README.md: -------------------------------------------------------------------------------- 1 | # System Utility app 2 | 3 | A system utility app built with NodeGUI by Siegfried 4 | 5 | Blog post: https://blog.logrocket.com/electron-alternatives-exploring-nodegui-and-react-nodegui/ 6 | 7 | Link: https://github.com/siegfriedgrimbeek/NodeGUI-System-Utility-Library 8 | 9 | 10 | -------------------------------------------------------------------------------- /react-nodegui/calculator/README.md: -------------------------------------------------------------------------------- 1 | # Calculator app 2 | 3 | This example showcases how to build a basic calculator clone. 4 | 5 | ### Screenshots 6 | 7 | **Linux** 8 | 9 | demo_linux 10 | 11 | **Windows** 12 | 13 | demo_win 14 | 15 | **Mac:** 16 | 17 | demo_mac 18 | 19 | To run the demo: 20 | 21 | 1. `npm install` 22 | 23 | 2. `npm start` 24 | -------------------------------------------------------------------------------- /react-nodegui/calculator/calculator_linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/calculator/calculator_linux.png -------------------------------------------------------------------------------- /react-nodegui/calculator/calculator_mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/calculator/calculator_mac.png -------------------------------------------------------------------------------- /react-nodegui/calculator/calculator_win.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/calculator/calculator_win.jpg -------------------------------------------------------------------------------- /react-nodegui/calculator/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useReducer, Reducer } from "react"; 2 | import { Renderer, View, Text, Button, Window } from "@nodegui/react-nodegui"; 3 | import { 4 | QPushButtonEvents, 5 | QMainWindowEvents, 6 | QWidgetEvents, 7 | QKeyEvent, 8 | NativeEvent 9 | } from "@nodegui/nodegui"; 10 | 11 | interface state { 12 | display: string; 13 | total: number; 14 | pendingOp: string; 15 | valueBuffer: string; 16 | } 17 | interface action { 18 | type: "operation" | "value"; 19 | value: string; 20 | } 21 | const initialState: state = { 22 | display: "", 23 | total: 0, 24 | pendingOp: "~", 25 | valueBuffer: "" 26 | }; 27 | 28 | const reducer: Reducer = (state, action) => { 29 | const newState = { ...state }; 30 | switch (action.type) { 31 | case "operation": { 32 | switch (newState.pendingOp) { 33 | case "+": { 34 | newState.total = 35 | newState.total + parseFloat(state.valueBuffer || "0"); 36 | break; 37 | } 38 | case "-": { 39 | newState.total = 40 | newState.total - parseFloat(state.valueBuffer || "0"); 41 | break; 42 | } 43 | case "*": { 44 | newState.total = 45 | newState.total * parseFloat(state.valueBuffer || "0"); 46 | break; 47 | } 48 | case "/": { 49 | newState.total = 50 | newState.total / parseFloat(state.valueBuffer || "1"); 51 | break; 52 | } 53 | case "=": { 54 | break; 55 | } 56 | case "~": { 57 | newState.total = parseFloat(state.valueBuffer || "0"); 58 | } 59 | default: 60 | } 61 | newState.valueBuffer = ""; 62 | newState.display = action.value; 63 | if (action.value === "=") { 64 | const total = newState.total; 65 | Object.assign(newState, initialState); 66 | newState.total = total; 67 | newState.display = `${total}`; 68 | } 69 | if (action.value === "~") { 70 | Object.assign(newState, initialState); 71 | } 72 | newState.pendingOp = `${action.value}`; 73 | break; 74 | } 75 | case "value": { 76 | if (state.pendingOp === "=") { 77 | newState.pendingOp = "~"; 78 | } 79 | if (!state.valueBuffer) { 80 | newState.display = action.value; 81 | newState.valueBuffer = `${action.value}`; 82 | } else { 83 | newState.display = `${state.display}` + `${action.value}`; 84 | newState.valueBuffer += `${action.value}`; 85 | } 86 | break; 87 | } 88 | default: 89 | throw new Error("Invalid operation"); 90 | } 91 | return newState; 92 | }; 93 | 94 | const App = () => { 95 | const [state, dispatch] = useReducer(reducer, initialState); 96 | const onOperator = (value: string) => () => { 97 | dispatch({ type: "operation", value }); 98 | }; 99 | const onValue = (value: string) => () => { 100 | dispatch({ type: "value", value }); 101 | }; 102 | const onKeyRelease = (evt: NativeEvent) => { 103 | const operatorKeys = ["~", "/", "*", "-", "=", "+"]; 104 | const valueKeys = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "."]; 105 | const keyEvt = new QKeyEvent(evt); 106 | const keyText = keyEvt.text(); 107 | if (operatorKeys.includes(keyText)) { 108 | dispatch({ type: "operation", value: keyText }); 109 | } else if (valueKeys.includes(keyText)) { 110 | dispatch({ type: "value", value: keyText }); 111 | } 112 | }; 113 | 114 | return ( 115 | <> 116 | 124 | 125 | 126 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /react-nodegui/react-router-example/src/pages/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Text, View, Button, useEventHandler } from "@nodegui/react-nodegui"; 3 | import { useHistory } from "react-router"; 4 | import { QPushButtonSignals } from "@nodegui/nodegui"; 5 | 6 | export default function Home() { 7 | const history = useHistory(); 8 | const handler = useEventHandler( 9 | { "clicked": () => history.push("/about") }, 10 | [] 11 | ); 12 | return ( 13 | 20 | Home Page 21 | You are now looking at Home Page 🤓 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /react-nodegui/react-router-example/src/routes.tsx: -------------------------------------------------------------------------------- 1 | import { Route } from "react-router"; 2 | import React from "react"; 3 | import Home from "./pages/Home"; 4 | import About from "./pages/About"; 5 | 6 | export default function AppRoutes() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /react-nodegui/react-router-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "jsx": "react", 6 | "strict": true, 7 | "alwaysStrict": true, 8 | "moduleResolution": "node", 9 | "esModuleInterop": true 10 | }, 11 | "include": ["**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /react-nodegui/react-router-example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); 4 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 5 | 6 | module.exports = (env, argv) => { 7 | const config = { 8 | mode: "production", 9 | entry: ["./src/index.tsx"], 10 | target: "node", 11 | output: { 12 | path: path.resolve(__dirname, "dist"), 13 | filename: "index.js" 14 | }, 15 | node: { 16 | __dirname: false, 17 | __filename: false 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.(j|t)sx?$/, 23 | exclude: /node_modules/, 24 | use: { 25 | loader: "babel-loader", 26 | options: { cacheDirectory: true, cacheCompression: false } 27 | } 28 | }, 29 | { 30 | test: /\.(png|jpe?g|gif|svg|bmp)$/i, 31 | use: [{ loader: "file-loader" }] 32 | }, 33 | { 34 | test: /\.node/i, 35 | use: [{ loader: "node-loader" }, { loader: "file-loader" }] 36 | } 37 | ] 38 | }, 39 | plugins: [], 40 | resolve: { 41 | extensions: [".tsx", ".ts", ".js", ".jsx", ".json"] 42 | } 43 | }; 44 | 45 | if (argv.mode === "development") { 46 | config.mode = "development"; 47 | config.plugins.push(new webpack.HotModuleReplacementPlugin()); 48 | config.plugins.push(new ForkTsCheckerWebpackPlugin()); 49 | config.devtool = "source-map"; 50 | config.watch = true; 51 | config.entry.unshift("webpack/hot/poll?100"); 52 | } 53 | 54 | if (argv.p) { 55 | config.plugins.push(new CleanWebpackPlugin()); 56 | } 57 | return config; 58 | }; 59 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | *.log -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/README.md: -------------------------------------------------------------------------------- 1 | # Weather App widget 2 | 3 | This example showcases how to build a basic weather widget app. 4 | 5 | ### Screenshots 6 | 7 | **Mac:** 8 | 9 | demo_mac 10 | 11 | To run the demo: 12 | 13 | 0. `cd weather-app-widget` 14 | 15 | 1. `npm install` 16 | 17 | 1. `npm start` 18 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-nodegui-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Atul R ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "start": "tsc && qode ./dist/index.js" 10 | }, 11 | "dependencies": { 12 | "@nodegui/nodegui": "^0.1.3", 13 | "@nodegui/react-nodegui": "^0.1.1", 14 | "react": "^16.9.0", 15 | "axios": "^0.19.0", 16 | "dateformat": "^3.0.3" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^12.6.8", 20 | "@types/react": "^16.9.1", 21 | "typescript": "^3.5.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/01d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/01d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/01n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/01n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/02d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/02d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/02n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/02n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/03d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/03d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/03n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/03n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/04d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/04d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/04n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/04n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/09d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/09d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/09n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/09n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/10d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/10d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/10n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/10n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/11d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/11d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/11n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/11n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/13d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/13d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/13n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/13n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/50d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/50d.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/50n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/50n.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/assets/icons/na.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodegui/examples/24233911a5b36977dd08bd16d8175413aac0ce82/react-nodegui/weather-app-widget/src/assets/icons/na.png -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/components/PlaceDate.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "@nodegui/react-nodegui"; 3 | import { dateFormatter } from "../utils/helpers"; 4 | 5 | type PlaceDateProps = { 6 | place: string; 7 | date: Date; 8 | }; 9 | export const PlaceDate: React.FC = props => { 10 | return ( 11 | 12 | {props.place} 13 | {dateFormatter(props.date)} 14 | 15 | ); 16 | }; 17 | 18 | const placeStyle = ` 19 | flex: 1; 20 | font-size: 20px; 21 | qproperty-alignment: 'AlignCenter'; 22 | color: white; 23 | `; 24 | 25 | const dateStyle = ` 26 | flex: 1; 27 | font-size: 12px; 28 | qproperty-alignment: 'AlignCenter'; 29 | color: white; 30 | `; 31 | 32 | const containerStyle = ` 33 | flex: 1; 34 | `; 35 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/components/Summary.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "@nodegui/react-nodegui"; 3 | 4 | type SummaryProps = { 5 | title: string; 6 | description: string; 7 | }; 8 | //https://doc.qt.io/qt-5/04-qdoc-commands-textmarkup.html 9 | export const Summary: React.FC = props => { 10 | return ( 11 | 12 | {`${props.title}: ${ 13 | props.description 14 | }.`} 15 | 16 | ); 17 | }; 18 | 19 | const containerStyle = ` 20 | align-items: 'center'; 21 | `; 22 | 23 | const textStyle = ` 24 | color: white; 25 | `; 26 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/components/TemperatureBox.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "@nodegui/react-nodegui"; 3 | 4 | type TempProps = { 5 | now: number; 6 | min: number; 7 | max: number; 8 | }; 9 | export const TemperatureBox: React.FC = props => { 10 | return ( 11 | 12 | {`${props.now} oC`} 13 | 14 | {`${props.min} oC / ${ 15 | props.max 16 | } oC`} 17 | 18 | 19 | ); 20 | }; 21 | 22 | const currentTempStyle = ` 23 | font-size: 20px; 24 | width: 100px; 25 | qproperty-alignment: AlignCenter; 26 | color: white; 27 | `; 28 | 29 | const temperatureBoxStyle = ` 30 | border-right: 1px solid white; 31 | flex: 1; 32 | align-items: 'center'; 33 | justify-content: 'center'; 34 | `; 35 | 36 | const smallBox = ` 37 | flex-direction: 'row'; 38 | align-items: 'center'; 39 | justify-content: 'center'; 40 | `; 41 | 42 | const smallInfo = ` 43 | width: 150px; 44 | color: white; 45 | qproperty-alignment: AlignCenter; 46 | `; 47 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/components/WeatherIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Image } from "@nodegui/react-nodegui"; 3 | import path from "path"; 4 | import { AspectRatioMode } from "@nodegui/nodegui"; 5 | 6 | const rootDir = path.resolve(__dirname, "../.."); 7 | const assetUrl = path.resolve(rootDir, "src/assets/icons"); 8 | 9 | type WeatherIconProps = { 10 | icon: string; 11 | [key: string]: any; 12 | }; 13 | export const WeatherIcon = React.memo(props => { 14 | const iconId = props.icon || "na"; 15 | const imageUrl = `${path.resolve(assetUrl, `${iconId}.png`)}`; 16 | return ( 17 | 22 | ); 23 | }); 24 | -------------------------------------------------------------------------------- /react-nodegui/weather-app-widget/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Renderer, 3 | Window, 4 | View, 5 | Button, 6 | useEventHandler 7 | } from "@nodegui/react-nodegui"; 8 | import React, { useEffect, useRef, useState, useCallback } from "react"; 9 | import { 10 | QMainWindow, 11 | WidgetAttribute, 12 | WindowType, 13 | QApplication, 14 | QPushButtonEvents 15 | } from "@nodegui/nodegui"; 16 | import os from 'os'; 17 | import { getCurrentWeather } from "./utils/weather"; 18 | import { WeatherIcon } from "./components/WeatherIcon"; 19 | import { TemperatureBox } from "./components/TemperatureBox"; 20 | import { Summary } from "./components/Summary"; 21 | import { PlaceDate } from "./components/PlaceDate"; 22 | 23 | const App = () => { 24 | const winRef = useRef(null); 25 | const [weather, setWeather] = useState(defaultState); 26 | useEffect(() => { 27 | if (winRef.current) { 28 | const win = winRef.current; 29 | initWindow(win); 30 | } 31 | getWeather(); 32 | }, []); 33 | 34 | const getWeather = useCallback(async () => { 35 | try { 36 | const data = await getCurrentWeather(); 37 | setWeather(data); 38 | } catch (err) { 39 | console.log(err); 40 | } 41 | }, []); 42 | 43 | const summary = weather.weather[0] || {}; 44 | const refreshHandler = useEventHandler( 45 | { 46 | [QPushButtonEvents.clicked]: async () => { 47 | setWeather(defaultState); 48 | await getWeather(); 49 | } 50 | }, 51 | [] 52 | ); 53 | const quitHandler = useEventHandler( 54 | { 55 | [QPushButtonEvents.clicked]: () => { 56 | QApplication.instance().quit(); 57 | } 58 | }, 59 | [] 60 | ); 61 | 62 | return ( 63 | 64 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 | 77 |