├── README.md ├── assets ├── Activity.png ├── Calendar.png ├── Chat.png ├── Teams.png └── loading@2x.gif ├── gui.node ├── index.html └── main.js /README.md: -------------------------------------------------------------------------------- 1 | # Electron with Native UI demo 2 | 3 | This demo shows how an app would look like with mixed uses of WebContents and 4 | Native UI. 5 | 6 | __This demo can only run on macOS with Electron 8.__ 7 | 8 | ## Steps 9 | 10 | 1. Clone this repo. 11 | 2. `npm install -g electron@8` 12 | 3. `electron main.js` 13 | 14 | ## About the `gui.node` 15 | 16 | The native UI bindings are provided by the https://github.com/yue/yue project, 17 | documentations can be found at https://libyue.com/docs/latest/js/. 18 | 19 | The `gui.node` is a custom native module compiled targeting Electron 8.3.0. 20 | (The official prebuilt binaries are only targeting Node.js.) 21 | -------------------------------------------------------------------------------- /assets/Activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/assets/Activity.png -------------------------------------------------------------------------------- /assets/Calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/assets/Calendar.png -------------------------------------------------------------------------------- /assets/Chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/assets/Chat.png -------------------------------------------------------------------------------- /assets/Teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/assets/Teams.png -------------------------------------------------------------------------------- /assets/loading@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/assets/loading@2x.gif -------------------------------------------------------------------------------- /gui.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcbenz/electron_yue/8fb863d6732e16ef1435e387c848f92480618838/gui.node -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const {app, webContents} = require('electron') 3 | const gui = require('./gui') 4 | 5 | const SIDEBAR_WIDTH = 68 6 | const ITEM_HEIGHT = 56 7 | 8 | global.win = null 9 | global.page = null 10 | 11 | app.once('ready', () => { 12 | // Create window. 13 | win = gui.Window.create({}) 14 | win.setContentSize({width: 600, height: 500}) 15 | win.onClose = () => app.quit() 16 | 17 | // The content view. 18 | const contentView = gui.Container.create() 19 | contentView.setStyle({flexDirection: 'row'}) 20 | win.setContentView(contentView) 21 | 22 | // The sidebar. 23 | const sidebar = gui.Container.create() 24 | sidebar.setStyle({ 25 | flexDirection: 'column', 26 | width: SIDEBAR_WIDTH, 27 | }) 28 | sidebar.setBackgroundColor('#313245') 29 | createSidebarItems(sidebar) 30 | contentView.addChildView(sidebar) 31 | 32 | // The loading indicator. 33 | const loadingIndicator = gui.GifPlayer.create() 34 | loadingIndicator.setStyle({ 35 | flex: 1, 36 | }) 37 | const gifPath = path.join(__dirname, 'assets', 'loading@2x.gif') 38 | loadingIndicator.setImage(gui.Image.createFromPath(gifPath)) 39 | loadingIndicator.setAnimating(true) 40 | contentView.addChildView(loadingIndicator) 41 | 42 | // Use Electron's WebContents. 43 | page = webContents.create({nodeIntegration: true}) 44 | // Make the loading indicator show for a while. 45 | setTimeout(() => { page.loadFile(__dirname + '/index.html') }, 1000) 46 | 47 | // Replace loading indicator with webContents after page is loaded. 48 | page.once('did-finish-load', () => { 49 | const chrome = gui.ChromeView.create(page.getNativeView()) 50 | chrome.setStyle({flex: 1}) 51 | contentView.removeChildView(loadingIndicator) 52 | contentView.addChildView(chrome) 53 | }) 54 | 55 | // Show window. 56 | win.center() 57 | win.activate() 58 | }) 59 | 60 | function createSidebarItems(sidebar) { 61 | const padding = 2 62 | const iconSize = 20 63 | const font = gui.Font.create('system-ui', 10, 'normal', 'normal') 64 | 65 | let selectedItem = null 66 | const items = ['Activity', 'Chat', 'Teams', 'Calendar'] 67 | for (const title of items) { 68 | const iconPath = path.join(__dirname, 'assets', title + '.png') 69 | const icon = gui.Image.createFromPath(iconPath) 70 | const text = gui.AttributedText.create(title, { 71 | color: '#FFF', 72 | align: 'center', 73 | font, 74 | }) 75 | const textBounds = text.getBoundsFor({width: SIDEBAR_WIDTH, height: ITEM_HEIGHT}) 76 | // Manually draw the sidebar item. 77 | const item = gui.Container.create() 78 | item.setStyle({ 79 | width: '100%', 80 | height: ITEM_HEIGHT, 81 | }) 82 | item.selected = false 83 | item.hover = false 84 | item.onDraw = (self, painter, dirty) => { 85 | // Background. 86 | if (item.selected || item.hover) { 87 | painter.setFillColor('#3B3E59') 88 | painter.fillRect(dirty) 89 | } 90 | if (item.selected) { 91 | painter.setFillColor('#E2E2F4') 92 | painter.fillRect({x: 0, y: 0, width: 4, height: ITEM_HEIGHT}) 93 | } 94 | // Text. 95 | text.setColor((item.selected || item.hover) ? '#FFF': '#999') 96 | painter.drawAttributedText(text, { 97 | x: 0, 98 | y: (ITEM_HEIGHT - padding - textBounds.height - iconSize) / 2 + iconSize + padding, 99 | width: SIDEBAR_WIDTH, 100 | height: textBounds.height, 101 | }) 102 | // Icon. 103 | painter.drawImage(icon, { 104 | x: (SIDEBAR_WIDTH - iconSize) / 2, 105 | y: (ITEM_HEIGHT - padding - textBounds.height - iconSize) / 2, 106 | width: iconSize, 107 | height: iconSize, 108 | }) 109 | } 110 | // Mouse events. 111 | item.onMouseEnter = () => { 112 | item.hover = true 113 | item.schedulePaint() 114 | } 115 | item.onMouseLeave = () => { 116 | item.hover = false 117 | item.schedulePaint() 118 | } 119 | item.onMouseUp = (self, event) => { 120 | if (event.button === 1) { 121 | if (selectedItem) { 122 | selectedItem.selected = false 123 | selectedItem.schedulePaint() 124 | selectedItem = item 125 | selectedItem.selected = true 126 | selectedItem.schedulePaint() 127 | } 128 | } 129 | } 130 | sidebar.addChildView(item) 131 | } 132 | 133 | // Select first item by default. 134 | selectedItem = sidebar.childAt(0) 135 | selectedItem.selected = true 136 | } 137 | --------------------------------------------------------------------------------