├── .gitbook.yaml
├── .gitignore
├── src
├── compiler
│ ├── directives
│ │ ├── index.js
│ │ └── model.js
│ └── index.js
├── runtime
│ ├── modules
│ │ ├── index.js
│ │ ├── attrs.js
│ │ └── events.js
│ ├── elements
│ │ ├── index.js
│ │ ├── text.js
│ │ ├── button.js
│ │ ├── box.js
│ │ ├── textinput.js
│ │ ├── window.js
│ │ ├── element.js
│ │ └── widget.js
│ ├── nodes
│ │ ├── comment.js
│ │ └── textnode.js
│ ├── patch.js
│ ├── index.js
│ └── node-ops.js
└── util
│ └── index.js
├── docs
├── .gitbook
│ └── assets
│ │ ├── vuido-example.png
│ │ ├── vuido-example (1).png
│ │ └── vuido-screenshot.png
├── components
│ ├── widgets
│ │ ├── text.md
│ │ ├── button.md
│ │ └── textinput.md
│ ├── containers
│ │ └── box.md
│ └── window.md
├── common-attributes.md
├── SUMMARY.md
├── installation
│ ├── README.md
│ └── manual-configuration.md
├── README.md
└── usage.md
├── .editorconfig
├── example
├── main.js
├── build
│ └── webpack.config.js
└── components
│ └── MainWindow.vue
├── .babelrc
├── packages
└── vuido-template-compiler
│ ├── README.md
│ └── package.json
├── LICENSE
├── package.json
└── README.md
/.gitbook.yaml:
--------------------------------------------------------------------------------
1 | root: ./docs/
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | dist/*.js
3 | example/dist/*.js
4 | node_modules
5 | packages/*/*.js
6 |
--------------------------------------------------------------------------------
/src/compiler/directives/index.js:
--------------------------------------------------------------------------------
1 | import model from './model'
2 |
3 | export default {
4 | model
5 | }
6 |
--------------------------------------------------------------------------------
/docs/.gitbook/assets/vuido-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abdelaziz18003/vuido/master/docs/.gitbook/assets/vuido-example.png
--------------------------------------------------------------------------------
/src/runtime/modules/index.js:
--------------------------------------------------------------------------------
1 | import attrs from './attrs'
2 | import events from './events'
3 |
4 | export default [ attrs, events ];
5 |
--------------------------------------------------------------------------------
/docs/.gitbook/assets/vuido-example (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abdelaziz18003/vuido/master/docs/.gitbook/assets/vuido-example (1).png
--------------------------------------------------------------------------------
/docs/.gitbook/assets/vuido-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abdelaziz18003/vuido/master/docs/.gitbook/assets/vuido-screenshot.png
--------------------------------------------------------------------------------
/src/runtime/elements/index.js:
--------------------------------------------------------------------------------
1 | export * from './window'
2 | export * from './box'
3 | export * from './text'
4 | export * from './textinput'
5 | export * from './button'
6 |
--------------------------------------------------------------------------------
/docs/components/widgets/text.md:
--------------------------------------------------------------------------------
1 | # Text
2 |
3 | Static widget which displays simple text.
4 |
5 | ## Example
6 |
7 | ```markup
8 | Hello, world!
9 | ```
10 |
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/docs/components/widgets/button.md:
--------------------------------------------------------------------------------
1 | # Button
2 |
3 | A simple button widget.
4 |
5 | ## Events
6 |
7 | ### click
8 |
9 | Called when the button is clicked.
10 |
11 | ## Example
12 |
13 | ```markup
14 |
15 | ```
16 |
17 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 | import Vue from 'vuido'
3 |
4 | import MainWindow from './components/MainWindow'
5 |
6 | const window = new Vue( {
7 | render( h ) {
8 | return h( MainWindow );
9 | }
10 | } );
11 |
12 | window.$mount();
13 |
14 | libui.startLoop();
15 |
--------------------------------------------------------------------------------
/src/runtime/elements/text.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 |
3 | import { Widget } from './widget'
4 |
5 | export class Text extends Widget {
6 | _createWidget() {
7 | this.widget = new libui.UiLabel();
8 | }
9 |
10 | _setWidgetText( text ) {
11 | this.widget.text = text;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/common-attributes.md:
--------------------------------------------------------------------------------
1 | # Common attributes
2 |
3 | The following attributes are shared by all widgets and containers.
4 |
5 | ### visible
6 |
7 | type: Boolean
8 |
9 | Whether the widget is visible or hidden.
10 |
11 | ### enabled
12 |
13 | type: Boolean
14 |
15 | Whether the widget is enabled or disabled.
16 |
17 |
--------------------------------------------------------------------------------
/src/runtime/nodes/comment.js:
--------------------------------------------------------------------------------
1 | export class Comment {
2 | constructor( text ) {
3 | this.parentNode = null;
4 | this.prevSibling = null;
5 | this.nextSibling = null;
6 |
7 | this.tagName = '';
8 |
9 | this.text = text;
10 | }
11 |
12 | setText( text ) {
13 | this.text = text;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/runtime/patch.js:
--------------------------------------------------------------------------------
1 | import { createPatchFunction } from 'core/vdom/patch'
2 | import baseModules from 'core/vdom/modules/index'
3 |
4 | import platformModules from './modules/index'
5 | import * as nodeOps from './node-ops'
6 |
7 | const modules = platformModules.concat( baseModules );
8 |
9 | export const patch = createPatchFunction( { nodeOps, modules } );
10 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [ "env", {
4 | "targets": {
5 | "node": 8
6 | },
7 | "modules": false,
8 | "useBuiltIns": true
9 | } ]
10 | ],
11 | "plugins": [
12 | "transform-object-rest-spread",
13 | "transform-class-properties",
14 | "transform-flow-strip-types"
15 | ],
16 | "comments": false
17 | }
18 |
--------------------------------------------------------------------------------
/src/runtime/nodes/textnode.js:
--------------------------------------------------------------------------------
1 | export class TextNode {
2 | constructor( text ) {
3 | this.parentNode = null;
4 | this.prevSibling = null;
5 | this.nextSibling = null;
6 |
7 | this.tagName = '';
8 |
9 | this.text = text;
10 | }
11 |
12 | setText( text ) {
13 | this.text = text;
14 |
15 | if ( this.parentNode != null )
16 | this.parentNode._setContentText( text );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/vuido-template-compiler/README.md:
--------------------------------------------------------------------------------
1 | # vuido-template-compiler
2 |
3 | Template compiler for [Vuido](https://github.com/mimecorg/vuido).
4 |
5 | You must use at least version 15.0 of [vue-loader](https://github.com/vuejs/vue-loader) in order to be able to inject the Vuido template compiler. Otherwise it will use the standard Vue.js template compiler which is not compatible with Vuido.
6 |
7 | This package is auto-generated. Refer to the Vuido documentation for more information.
8 |
--------------------------------------------------------------------------------
/src/runtime/elements/button.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 |
3 | import { Widget } from './widget'
4 |
5 | export class Button extends Widget {
6 | _createWidget() {
7 | this.widget = new libui.UiButton();
8 | }
9 |
10 | _setWidgetText( text ) {
11 | this.widget.text = text;
12 | }
13 |
14 | _setWidgetHandler( event, handler ) {
15 | if ( event == 'click' )
16 | this.widget.onClicked( handler );
17 | else
18 | super._setWidgetHandler( event, handler );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 |
3 | * [Vuido](README.md)
4 | * [Installation](installation/README.md)
5 | * [Manual configuration](installation/manual-configuration.md)
6 | * [Usage](usage.md)
7 | * [Common attributes](common-attributes.md)
8 | * Components
9 | * [Window](components/window.md)
10 | * Containers
11 | * [Box](components/containers/box.md)
12 | * Widgets
13 | * [Button](components/widgets/button.md)
14 | * [Text](components/widgets/text.md)
15 | * [TextInput](components/widgets/textinput.md)
16 |
17 |
--------------------------------------------------------------------------------
/docs/components/widgets/textinput.md:
--------------------------------------------------------------------------------
1 | # TextInput
2 |
3 | An input widget for editing a single line of text.
4 |
5 | TextInput supports the `v-model` directive.
6 |
7 | ## Attributes
8 |
9 | ### text
10 |
11 | The current text displayed in the widget.
12 |
13 | ### readonly
14 |
15 | Whether the widget is read-only or editable.
16 |
17 | ## Events
18 |
19 | ### input
20 |
21 | Called when the text is changed. The current text is passed as an argument.
22 |
23 | ## Example
24 |
25 | ```markup
26 |
27 | ```
28 |
29 |
--------------------------------------------------------------------------------
/src/util/index.js:
--------------------------------------------------------------------------------
1 | import { makeMap } from 'shared/util'
2 |
3 | export function isUnaryTag( el ) {
4 | return false;
5 | }
6 |
7 | export function canBeLeftOpenTag( el ) {
8 | return false;
9 | }
10 |
11 | export function mustUseProp( tag, type, name ) {
12 | return false;
13 | }
14 |
15 | export const isReservedTag = makeMap( 'template,script,style,box,button,text,textinput,window', true );
16 |
17 | export function getTagNamespace( tag ) {
18 | }
19 |
20 | export function isUnknownElement( tag ) {
21 | return false;
22 | }
23 |
24 | export const isBooleanAttr = makeMap( 'visible,enabled,stretchy,margined,padded,horizontal,readonly' );
25 |
--------------------------------------------------------------------------------
/packages/vuido-template-compiler/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuido-template-compiler",
3 | "version": "0.1.0",
4 | "description": "Template compiler for Vuido",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/mimecorg/vuido.git"
9 | },
10 | "keywords": [
11 | "vuido",
12 | "compiler"
13 | ],
14 | "author": "Michał Męciński",
15 | "license": "MIT",
16 | "bugs": {
17 | "url": "https://github.com/mimecorg/vuido/issues"
18 | },
19 | "homepage": "https://github.com/mimecorg/vuido",
20 | "dependencies": {
21 | "de-indent": "^1.0.2",
22 | "he": "^1.1.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/compiler/index.js:
--------------------------------------------------------------------------------
1 | import { parseComponent } from 'sfc/parser'
2 | import { createCompiler } from 'compiler/index'
3 | import { genStaticKeys } from 'shared/util'
4 |
5 | import { isUnaryTag, canBeLeftOpenTag, mustUseProp, isReservedTag, getTagNamespace } from '../util'
6 |
7 | import directives from './directives'
8 |
9 | const modules = [];
10 |
11 | const baseOptions = {
12 | modules,
13 | directives,
14 | isUnaryTag,
15 | canBeLeftOpenTag,
16 | mustUseProp,
17 | isReservedTag,
18 | getTagNamespace,
19 | preserveWhitespace: false,
20 | staticKeys: genStaticKeys( modules )
21 | };
22 |
23 | const { compile, compileToFunctions } = createCompiler( baseOptions );
24 |
25 | export {
26 | parseComponent,
27 | compile,
28 | compileToFunctions
29 | }
30 |
--------------------------------------------------------------------------------
/docs/components/containers/box.md:
--------------------------------------------------------------------------------
1 | # Box
2 |
3 | A container which arranges all children vertically or horizontally.
4 |
5 | ## Attributes
6 |
7 | ### horizontal
8 |
9 | type: Boolean
10 |
11 | Whether the box arranges its children vertically or horizontally.
12 |
13 | ### padded
14 |
15 | type: Boolean
16 |
17 | Whether the box adds some padding between its children.
18 |
19 | ## Child attributes
20 |
21 | The following attributes can be added to child components to change the way they are handled by the box container.
22 |
23 | ### stretchy
24 |
25 | type: Boolean
26 |
27 | Whether the child stretches to fill available space.
28 |
29 | ## Example
30 |
31 | ```markup
32 |
33 |
34 |
35 |
36 | ```
37 |
38 |
--------------------------------------------------------------------------------
/src/compiler/directives/model.js:
--------------------------------------------------------------------------------
1 | import { addHandler, addAttr } from 'compiler/helpers'
2 | import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'
3 |
4 | import { isReservedTag } from '../../util'
5 |
6 | export default function model( el, dir, _warn ) {
7 | if ( el.component != null || !isReservedTag( el.tag ) )
8 | genComponentModel( el, dir.value, dir.modifiers );
9 | else if ( el.tag == 'TextInput' )
10 | genDefaultModel( el, dir.value, dir.modifiers );
11 | else if ( process.env.NODE_ENV != 'production' )
12 | _warn( el.tag + ' does not support v-model' );
13 | }
14 |
15 | function genDefaultModel( el, value, modifiers ) {
16 | const { trim, number } = modifiers || {};
17 |
18 | let valueExpression = '$event';
19 | if ( trim )
20 | valueExpression += '.trim()';
21 | if ( number )
22 | valueExpression = '_n(' + valueExpression + ')';
23 |
24 | const code = genAssignmentCode( value, valueExpression );
25 |
26 | addAttr( el, 'value', '(' + value + ')' );
27 | addHandler( el, 'input', code, null, true );
28 | }
29 |
--------------------------------------------------------------------------------
/docs/components/window.md:
--------------------------------------------------------------------------------
1 | # Window
2 |
3 | The root of the Vue.js application must be a Window component which represents a single window.
4 |
5 | The Window component must contain exactly one child widget, typically a container.
6 |
7 | ## Attributes
8 |
9 | ### title
10 |
11 | type: String
12 |
13 | The title of the window.
14 |
15 | ### width
16 |
17 | type: Number
18 |
19 | The width of the window.
20 |
21 | ### height
22 |
23 | type: Number
24 |
25 | The height of the window
26 |
27 | ### margined
28 |
29 | type: Boolean
30 |
31 | Whether the window adds a margin around its children.
32 |
33 | ### menu
34 |
35 | type: Boolean
36 |
37 | Whether the window has a menu.
38 |
39 | ## Events
40 |
41 | ### close
42 |
43 | Called when the close button is clicked. To exit the application, call `libui.stopLoop()`. To close the window without exiting the application, call `this.$root.$destroy()`.
44 |
45 | ## Example
46 |
47 | ```markup
48 |
49 |
50 | ...
51 |
52 |
53 | ```
54 |
55 |
--------------------------------------------------------------------------------
/src/runtime/elements/box.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 |
3 | import { Widget } from './widget'
4 |
5 | export class Box extends Widget {
6 | _getDefaultAttributes() {
7 | return {
8 | ...super._getDefaultAttributes(),
9 | horizontal: false,
10 | padded: false
11 | };
12 | }
13 |
14 | _createWidget() {
15 | if ( this.attributes.horizontal )
16 | this.widget = new libui.UiHorizontalBox();
17 | else
18 | this.widget = new libui.UiVerticalBox();
19 | }
20 |
21 | _appendWidget( childNode ) {
22 | this.widget.append( childNode.widget, childNode.attributes.stretchy );
23 | }
24 |
25 | _removeWidget( childNode ) {
26 | this.widget.deleteAt( childNode.widgetIndex );
27 | }
28 |
29 | _initializeWidgetAttributes() {
30 | super._initializeWidgetAttributes();
31 |
32 | if ( this.attributes.padded )
33 | this.widget.padded = true;
34 | }
35 |
36 | _setWidgetAttribute( key, value ) {
37 | if ( key == 'padded' )
38 | this.widget.padded = value;
39 | else
40 | super._setWidgetAttribute( key, value );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (C) 2018 Michał Męciński
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/runtime/modules/attrs.js:
--------------------------------------------------------------------------------
1 | import { extend } from 'shared/util'
2 | import { isBooleanAttr } from '../../util'
3 |
4 | function updateAttrs( oldVnode, vnode ) {
5 | if ( oldVnode.data.attrs == null && vnode.data.attrs == null )
6 | return;
7 |
8 | const elm = vnode.elm;
9 | const oldAttrs = oldVnode.data.attrs || {};
10 | let attrs = vnode.data.attrs || {};
11 |
12 | if ( attrs.__ob__ != null )
13 | attrs = vnode.data.attrs = extend( {}, attrs );
14 |
15 | for ( let key in attrs ) {
16 | let cur = attrs[ key ];
17 | let old = oldAttrs[ key ];
18 | if ( old !== cur )
19 | setAttr( elm, key, cur );
20 | }
21 |
22 | for ( let key in oldAttrs ) {
23 | if ( attrs[ key ] == null )
24 | elm.setAttribute( key, null );
25 | }
26 | }
27 |
28 | function setAttr( elm, key, value ) {
29 | if ( isBooleanAttr( key ) ) {
30 | if ( value == null || value === false )
31 | elm.setAttribute( key, false );
32 | else
33 | elm.setAttribute( key, true );
34 | } else {
35 | elm.setAttribute( key, value );
36 | }
37 | }
38 |
39 | export default {
40 | create: updateAttrs,
41 | update: updateAttrs
42 | }
43 |
--------------------------------------------------------------------------------
/example/build/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require( 'path' );
2 | const webpack = require( 'webpack' );
3 | const VueLoaderPlugin = require( 'vue-loader/lib/plugin' );
4 |
5 | const VuidoTemplateCompiler = require( '../../packages/vuido-template-compiler' );
6 |
7 | module.exports = {
8 | entry: './example/main.js',
9 | output: {
10 | path: path.resolve( __dirname, '../dist' ),
11 | filename: 'main.js'
12 | },
13 | target: 'node',
14 | module: {
15 | rules: [
16 | {
17 | test: /\.vue$/,
18 | loader: 'vue-loader',
19 | options: {
20 | compiler: VuidoTemplateCompiler
21 | }
22 | },
23 | {
24 | test: /\.js$/,
25 | loader: 'babel-loader',
26 | exclude: /node_modules/
27 | }
28 | ]
29 | },
30 | resolve: {
31 | extensions: [ '.js', '.vue', '.json' ]
32 | },
33 | plugins: [
34 | new webpack.ExternalsPlugin( 'commonjs', [ 'libui-node', { 'vuido': '../../dist/vuido.js' } ] ),
35 | new VueLoaderPlugin()
36 | ],
37 | performance: {
38 | hints: false
39 | },
40 | stats: {
41 | children: false,
42 | modules: false
43 | },
44 | devtool: false
45 | };
46 |
--------------------------------------------------------------------------------
/src/runtime/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'core/instance/index'
2 | import { initGlobalAPI } from 'core/global-api/index'
3 | import { mountComponent } from 'core/instance/lifecycle'
4 |
5 | import { patch } from './patch'
6 |
7 | import { mustUseProp, isReservedTag, getTagNamespace, isUnknownElement } from '../util'
8 |
9 | initGlobalAPI( Vue );
10 |
11 | Vue.version = __VUE_VERSION__;
12 |
13 | Vue.config.mustUseProp = mustUseProp;
14 | Vue.config.isReservedTag = isReservedTag;
15 | Vue.config.getTagNamespace = getTagNamespace;
16 | Vue.config.isUnknownElement = isUnknownElement;
17 |
18 | Vue.prototype.__patch__ = patch;
19 |
20 | Vue.prototype.$mount = function( el, hydrating ) {
21 | if ( el != null )
22 | throw new Error( 'Mount element is not supported' );
23 |
24 | mountComponent( this, el, hydrating );
25 |
26 | if ( this.$parent == null && this.$el.widget == null ) {
27 | if ( this.$el.tagName != 'Window' )
28 | throw new Error( 'Top level element must be a Window' );
29 |
30 | this.$el._mountWindow();
31 |
32 | this.$on( 'hook:destroyed', () => {
33 | this.$el._destroyWindow();
34 | } );
35 | }
36 |
37 | return this;
38 | };
39 |
40 | export default Vue
41 |
--------------------------------------------------------------------------------
/src/runtime/modules/events.js:
--------------------------------------------------------------------------------
1 | import { updateListeners } from 'core/vdom/helpers/update-listeners'
2 |
3 | let target = null;
4 |
5 | function add( event, handler, once, capture ) {
6 | if ( capture )
7 | throw new Error( 'Bubble phase events are not supported' );
8 |
9 | if ( once ) {
10 | const oldHandler = handler;
11 | const _target = target;
12 |
13 | handler = function( ev ) {
14 | const res = arguments.length === 1 ? oldHandler( ev ) : oldHandler.apply( null, arguments );
15 | if ( res !== null )
16 | remove( event, null, null, _target );
17 | }
18 | }
19 |
20 | target.addEventListener( event, handler );
21 | }
22 |
23 | function remove( event, handler, capture, _target ) {
24 | ( _target || target ).removeEventListener( event );
25 | }
26 |
27 | function updateDOMListeners( oldVnode, vnode ) {
28 | if ( oldVnode.data.on == null && vnode.data.on == null )
29 | return;
30 |
31 | const on = vnode.data.on || {};
32 | const oldOn = oldVnode.data.on || {};
33 |
34 | target = vnode.elm;
35 | updateListeners( on, oldOn, add, remove, vnode.context );
36 | target = null;
37 | }
38 |
39 | export default {
40 | create: updateDOMListeners,
41 | update: updateDOMListeners
42 | }
43 |
--------------------------------------------------------------------------------
/src/runtime/elements/textinput.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 |
3 | import { Widget } from './widget'
4 |
5 | export class TextInput extends Widget {
6 | _getDefaultAttributes() {
7 | return {
8 | ...super._getDefaultAttributes(),
9 | value: '',
10 | readonly: false
11 | };
12 | }
13 |
14 | _createWidget() {
15 | this.widget = new libui.UiEntry();
16 | }
17 |
18 | _initializeWidgetAttributes() {
19 | super._initializeWidgetAttributes();
20 |
21 | if ( this.attributes.value != '' )
22 | this.widget.text = this.attributes.value;
23 | if ( this.attributes.readonly )
24 | this.widget.readOnly = true;
25 | }
26 |
27 | _setWidgetAttribute( key, value ) {
28 | if ( key == 'value' ) {
29 | if ( this.widget.text != value )
30 | this.widget.text = value;
31 | } else if ( key == 'readonly' ) {
32 | this.widget.readOnly = value;
33 | } else {
34 | super._setWidgetAttribute( key, value );
35 | }
36 | }
37 |
38 | _setWidgetHandler( event, handler ) {
39 | if ( event == 'input' ) {
40 | this.widget.onChanged( () => {
41 | handler( this.widget.text );
42 | } );
43 | } else {
44 | super._setWidgetHandler( event, handler );
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/docs/installation/README.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Prerequisites
4 |
5 | The following prerequisites are needed to compile [libui-node](https://github.com/parro-it/libui-node), which is used by Vuido.
6 |
7 | ### Windows
8 |
9 | * [windows-build-tools](https://www.npmjs.com/package/windows-build-tools) or Visual Studio 2015
10 | * [Visual C++ Redistributable Package for Visual Studio 2013](https://www.microsoft.com/en-us/download/details.aspx?id=40784)
11 |
12 | ### Linux
13 |
14 | If they are not provided by default in your distribution:
15 |
16 | * [build-essential](https://packages.ubuntu.com/xenial/build-essential)
17 | * [GTK+ 3](https://packages.ubuntu.com/source/xenial/gtk+3.0)
18 |
19 | ### OS X
20 |
21 | * Xcode
22 |
23 | ## Quick setup
24 |
25 | The easiest way to start using Vuido is to use [vue-cli](https://www.npmjs.com/package/vue-cli) to create the scaffolding of the project.
26 |
27 | First make sure that vue-cli is installed globally:
28 |
29 | ```bash
30 | npm install -g vue-cli
31 | ```
32 |
33 | Run the following command to create the project \(replace `my-project` with the name of your project\):
34 |
35 | ```bash
36 | vue init mimecorg/vuido-webpack-template my-project
37 | ```
38 |
39 | Enter the directory created by vue-cli and install all dependencies:
40 |
41 | ```bash
42 | cd my-project
43 | npm install
44 | ```
45 |
46 | Now you can build and run your application:
47 |
48 | ```bash
49 | npm run build
50 | npm start
51 | ```
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/runtime/node-ops.js:
--------------------------------------------------------------------------------
1 | import { TextNode } from './nodes/textnode'
2 | import { Comment } from './nodes/comment'
3 | import * as elements from './elements'
4 |
5 | export function createElement( tagName, vnode ) {
6 | const element = elements[ tagName ];
7 | if ( element == null )
8 | throw new Error( 'Unknown element ' + tagName );
9 | return new element( tagName );
10 | }
11 |
12 | export function createElementNS( namespace, tagName ) {
13 | throw new Error( 'Namespaced elements are not supported' );
14 | }
15 |
16 | export function createTextNode( text ) {
17 | return new TextNode( text );
18 | }
19 |
20 | export function createComment( text ) {
21 | return new Comment( text );
22 | }
23 |
24 | export function appendChild( node, child ) {
25 | node.appendChild( child );
26 | }
27 |
28 | export function insertBefore( parentNode, newNode, referenceNode ) {
29 | parentNode.insertBefore( newNode, referenceNode );
30 | }
31 |
32 | export function removeChild( node, child ) {
33 | node.removeChild( child );
34 | }
35 |
36 | export function parentNode( node ) {
37 | return node.parentNode;
38 | }
39 |
40 | export function nextSibling( node ) {
41 | return node.nextSibling;
42 | }
43 |
44 | export function tagName( node ) {
45 | return node.tagName;
46 | }
47 |
48 | export function setTextContent( node, text ) {
49 | node.setText( text );
50 | }
51 |
52 | export function setAttribute( node, key, value ) {
53 | node.setAttribute( key, value );
54 | }
55 |
56 | export function setStyleScope( node, scopeId ) {
57 | throw new Error( 'Scoped styles are not supported' );
58 | }
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuido",
3 | "version": "0.1.0",
4 | "description": "Native desktop applications using Vue.js",
5 | "main": "dist/vuido.js",
6 | "files": [
7 | "dist/*.js",
8 | "src"
9 | ],
10 | "scripts": {
11 | "build": "npm run build:runtime && npm run build:compiler && npm run build:example",
12 | "build:runtime": "webpack --config build/webpack.runtime.config.js",
13 | "build:compiler": "webpack --config build/webpack.compiler.config.js",
14 | "build:example": "webpack --config example/build/webpack.config.js",
15 | "example": "node ./example/dist/main.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/mimecorg/vuido.git"
20 | },
21 | "keywords": [
22 | "vuido",
23 | "vue",
24 | "libui",
25 | "desktop"
26 | ],
27 | "author": "Michał Męciński",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/mimecorg/vuido/issues"
31 | },
32 | "homepage": "https://github.com/mimecorg/vuido",
33 | "dependencies": {
34 | "libui-node": "^0.2.0"
35 | },
36 | "devDependencies": {
37 | "babel-core": "^6.26.3",
38 | "babel-loader": "^7.1.4",
39 | "babel-plugin-transform-class-properties": "^6.24.1",
40 | "babel-plugin-transform-flow-strip-types": "^6.22.0",
41 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
42 | "babel-preset-env": "^1.7.0",
43 | "de-indent": "^1.0.2",
44 | "he": "^1.1.1",
45 | "vue": "^2.5.16",
46 | "vue-loader": "^15.0.11",
47 | "vue-template-compiler": "^2.5.16",
48 | "webpack": "^3.12.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Vuido
2 |
3 | Create native desktop applications for Windows, OS X and Linux using Vue.js.
4 |
5 |  
6 |
7 | ## Introduction
8 |
9 | Vuido makes it possible to create lightweight, native desktop applications using Vue.js. Applications using Vuido can run on Windows, OS X and Linux, using native GUI components, and don't require Electron.
10 |
11 | 
12 |
13 | Under the hood, Vuido uses the [libui](https://github.com/andlabs/libui) library which provides native GUI components for each desktop platform, and the [libui-node](https://github.com/parro-it/libui-node) bindings for Node.js.
14 |
15 | Vuido supports most of the standard Vue.js API and it's compatible with many Vue.js extensions, for example Vuex. Applications using Vuido can also use all standard Node.js modules and any packages compatible with Node.js.
16 |
17 | ## Development status
18 |
19 | At the moment Vuido is in a very early stage of development. The first goal is to implement all controls currently supported by libui and to write a documentation.
20 |
21 | ## Acknowledgements
22 |
23 | Vuido is largely based on Vue.js and shares most of its code, except for the platform specific code related to libui.
24 |
25 | Vuido was inspired by [Proton Native](https://github.com/kusti8/proton-native), an environment for creating native desktop applications using React.
26 |
27 | ## License
28 |
29 | Vuido is licensed under the MIT license
30 |
31 | Copyright \(C\) 2018 Michał Męciński
32 |
33 |
--------------------------------------------------------------------------------
/example/components/MainWindow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Counter: {{ counter }}
10 |
11 |
12 |
13 |
14 | Value: {{ random }}
15 |
16 |
17 |
18 |
19 | {{ text }}
20 |
21 |
22 |
23 |
24 |
25 |
60 |
--------------------------------------------------------------------------------
/src/runtime/elements/window.js:
--------------------------------------------------------------------------------
1 | import libui from 'libui-node'
2 |
3 | import { Element } from './element'
4 | import { Widget } from './widget';
5 |
6 | export class Window extends Element {
7 | constructor( tagName ) {
8 | super( tagName );
9 |
10 | this.window = null;
11 | }
12 |
13 | appendChild( childNode ) {
14 | super.appendChild( childNode );
15 |
16 | if ( !( childNode instanceof Widget ) )
17 | throw new Error( 'Window can only contain child widgets' );
18 |
19 | if ( this.childNodes.length > 1 )
20 | throw new Error( 'Window can only contain one child element' );
21 |
22 | if ( this.window != null )
23 | throw new Error( 'Window child element cannot be inserted dynamically' );
24 | }
25 |
26 | insertBefore( childNode, referenceNode ) {
27 | throw new Error( 'Window child element cannot be inserted dynamically' );
28 | }
29 |
30 | removeChild( childNode ) {
31 | throw new Error( 'Window child element cannot be removed dynamically' );
32 | }
33 |
34 | setAttribute( key, value ) {
35 | super.setAttribute( key, value );
36 |
37 | if ( this.window != null )
38 | this._setWindowAttribute( key, value );
39 | }
40 |
41 | addEventListener( event, handler ) {
42 | super.addEventListener( event, handler );
43 |
44 | if ( this.window != null )
45 | this._setWindowHandler( event, handler );
46 | }
47 |
48 | removeEventListener( event ) {
49 | super.removeEventListener( event );
50 |
51 | if ( this.window != null )
52 | this._setWindowHandler( event, null );
53 | }
54 |
55 | _getDefaultAttributes() {
56 | return {
57 | title: 'Vuido',
58 | width: 400,
59 | height: 300,
60 | menu: false,
61 | margined: false
62 | };
63 | }
64 |
65 | _mountWindow() {
66 | this.window = new libui.UiWindow( this.attributes.title, this.attributes.width, this.attributes.height, this.attributes.menu );
67 |
68 | if ( this.attributes.margined )
69 | this.window.margined = true;
70 |
71 | for ( let key in this.handlers )
72 | this._setWindowHandler( key, this.handlers[ key ] );
73 |
74 | if ( this.childNodes.length > 0 ) {
75 | this.childNodes[ 0 ]._mountWidget();
76 | this.window.setChild( this.childNodes[ 0 ].widget );
77 | }
78 |
79 | this.window.show();
80 | }
81 |
82 | _destroyWindow() {
83 | this.window.close();
84 | this.window = null;
85 |
86 | this.childNodes = [];
87 | }
88 |
89 | _setWindowAttribute( key, value ) {
90 | throw new Error( 'Window does not have attribute ' + key );
91 | }
92 |
93 | _setWindowHandler( event, handler ) {
94 | if ( event == 'close' )
95 | this.window.onClosing( handler );
96 | else
97 | throw new Error( 'Window does not have event ' + event );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/docs/installation/manual-configuration.md:
--------------------------------------------------------------------------------
1 | # Manual configuration
2 |
3 | This information is intended for advanced users who wish to configure a Vuido application manually. In most cases the automatic [quick setup](./#quick-setup) should be enough.
4 |
5 | You need to install the [vuido](https://www.npmjs.com/package/vuido) package in order to use Vuido in you application:
6 |
7 | ```bash
8 | npm install --save vuido
9 | ```
10 |
11 | In order to use single-file components, you will also need [webpack](https://webpack.js.org/) and [vue-loader](https://vue-loader.vuejs.org/). The configuration is similar to a web application using Vue.js, with some important differences:
12 |
13 | * The [vuido-template-compiler](https://www.npmjs.com/package/vuido-template-compiler) must be used instead of the standard vue-template-compiler which is used by vue-loader by default. Use the `compiler` option to pass the Vuido compiler to vue-loader. Note that this option requires vue-loader v15 or newer.
14 | * Set the target option to 'node' to ensure that the compiled script can be run correctly by Node.js.
15 | * Use webpack.ExternalsPlugin to exclude libui-node and other native modules from the bundle.
16 |
17 | Example of webpack configuration using Vuido:
18 |
19 | {% code-tabs %}
20 | {% code-tabs-item title="webpack.config.js" %}
21 | ```javascript
22 | const path = require('path');
23 | const webpack = require('webpack');
24 | const VueLoaderPlugin = require('vue-loader/lib/plugin');
25 | const VuidoTemplateCompiler = require('vuido-template-compiler');
26 |
27 | module.exports = {
28 | entry: './src/main.js',
29 | output: {
30 | path: path.resolve(__dirname, '../dist'),
31 | filename: 'bundle.js'
32 | }
33 | target: 'node',
34 | module: {
35 | rules: [
36 | {
37 | test: /\.vue$/,
38 | loader: 'vue-loader',
39 | options: {
40 | compiler: VuidoTemplateCompiler
41 | }
42 | },
43 | {
44 | test: /\.js$/,
45 | loader: 'babel-loader',
46 | exclude: /node_modules/
47 | }
48 | ]
49 | },
50 | resolve: {
51 | extensions: ['.js', '.vue', '.json']
52 | },
53 | plugins: [
54 | new webpack.ExternalsPlugin('commonjs', ['libui-node']),
55 | new VueLoaderPlugin()
56 | ]
57 | };
58 | ```
59 | {% endcode-tabs-item %}
60 | {% endcode-tabs %}
61 |
62 | You also need to install [babel-core](https://www.npmjs.com/package/babel-core), [babel-loader](https://github.com/babel/babel-loader) and [babel-preset-env](https://www.npmjs.com/package/babel-preset-env).
63 |
64 | The basic Babel configuration which compiles the scripts for Node.js v8 looks like this:
65 |
66 | {% code-tabs %}
67 | {% code-tabs-item title=".babelrc" %}
68 | ```javascript
69 | {
70 | "presets": [
71 | [ "env", {
72 | "targets": {
73 | "node": 8
74 | },
75 | "modules": false,
76 | "useBuiltIns": true
77 | } ]
78 | ],
79 | "comments": false
80 | }
81 | ```
82 | {% endcode-tabs-item %}
83 | {% endcode-tabs %}
84 |
85 |
--------------------------------------------------------------------------------
/docs/usage.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | Vuido is simply a port of Vue.js for the desktop and it can be used just like Vue.js. Vuido supports most of the standard Vue.js API, including methods, components and directives. It's also compatible with many Vue.js extensions, for example Vuex.
4 |
5 | Applications using Vuido run in a Node.js environment, so they can use all standard Node.js modules, for example fs and http, and any packages compatible with Node.js.
6 |
7 | ## Creating a window component
8 |
9 | The easiest way to create a window component using Vuido is to use a single-file component with .vue extension. The component should include a template, where you can use Vuido components instead of HTML. It should also include a script which defines the component logic.
10 |
11 | Here's a basic window component using Vuido:
12 |
13 | {% code-tabs %}
14 | {% code-tabs-item title="MainWindow.vue" %}
15 | ```markup
16 |
17 |
18 |
19 | Counter: {{ counter }}
20 |
21 |
22 |
23 |
24 |
25 |
44 | ```
45 | {% endcode-tabs-item %}
46 | {% endcode-tabs %}
47 |
48 | ## The main script of the application
49 |
50 | A simple script with initializes the application looks as follows:
51 |
52 | {% code-tabs %}
53 | {% code-tabs-item title="main.js" %}
54 | ```javascript
55 | import libui from 'libui-node'
56 | import Vue from 'vuido'
57 |
58 | import MainWindow from './components/MainWindow'
59 |
60 | const window = new Vue({
61 | render: h => h(MainWindow)
62 | });
63 |
64 | window.$mount();
65 |
66 | libui.startLoop();
67 | ```
68 | {% endcode-tabs-item %}
69 | {% endcode-tabs %}
70 |
71 | After creating the window component, call `$mount()` to show the window.
72 |
73 | {% hint style="warning" %}
74 | Note that `$mount()` should be called without any parameters. Also you should not pass the `el` option to the Vue instance.
75 | {% endhint %}
76 |
77 | Call `libui.startLoop()` to start the event loop of the application. To exit the application, call `libui.stopLoop()`. This is usually done in response to the close event of the window.
78 |
79 | {% hint style="info" %}
80 | Your application can create multiple windows by creating and mounting multiple root components. In order to close a window without exiting the application, call `$destroy()` on the root component.
81 | {% endhint %}
82 |
83 | ## Compiling and running the application
84 |
85 | You must build your application before running it:
86 |
87 | ```bash
88 | npm run build
89 | ```
90 |
91 | Use the following command to run the application:
92 |
93 | ```bash
94 | npm start
95 | ```
96 |
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vuido
2 |
3 | Create native desktop applications for Windows, OS X and Linux using [Vue.js](https://vuejs.org/).
4 |
5 | [](https://npmjs.org/package/vuido)
6 | [](https://github.com/mimecorg/vuido/blob/master/LICENSE)
7 |
8 | ## Introduction
9 |
10 | Vuido makes it possible to create lightweight, native desktop applications using Vue.js. Application using Vuido can run on Windows, OS X and Linux, using native GUI components, and don't require Electron.
11 |
12 | 
13 |
14 | Under the hood, Vuido uses the [libui](https://github.com/andlabs/libui) library which provides native GUI components for each desktop platform, and the [libui-node](https://github.com/parro-it/libui-node) bindings for Node.js.
15 |
16 | Vuido supports most of the standard Vue.js API and it's compatible with many Vue.js extensions, for example Vuex. Applications using Vuido can also use all standard Node.js modules and any packages compatible with Node.js.
17 |
18 | ## Prerequisites
19 |
20 | The following prerequisites are needed to compile [libui-node](https://github.com/parro-it/libui-node), which is used by Vuido.
21 |
22 | ### Windows
23 |
24 | * [windows-build-tools](https://www.npmjs.com/package/windows-build-tools) or Visual Studio 2015
25 | * [Visual C++ Redistributable Package for Visual Studio 2013](https://www.microsoft.com/en-us/download/details.aspx?id=40784)
26 |
27 | ### Linux
28 |
29 | If they are not provided by default in your distribution:
30 |
31 | * [build-essential](https://packages.ubuntu.com/xenial/build-essential)
32 | * [GTK+ 3](https://packages.ubuntu.com/source/xenial/gtk+3.0)
33 |
34 | ### OS X
35 |
36 | * Xcode
37 |
38 | ## Quick setup
39 |
40 | The easiest way to start using Vuido is to use [vue-cli](https://www.npmjs.com/package/vue-cli) to create the scaffolding of the project.
41 |
42 | First make sure that vue-cli is installed globally:
43 |
44 | ```bash
45 | npm install -g vue-cli
46 | ```
47 |
48 | Run the following command to create the project \(replace `my-project` with the name of your project\):
49 |
50 | ```bash
51 | vue init mimecorg/vuido-webpack-template my-project
52 | ```
53 |
54 | Enter the directory created by vue-cli and install all dependencies:
55 |
56 | ```bash
57 | cd my-project
58 | npm install
59 | ```
60 |
61 | Now you can build and run your application:
62 |
63 | ```bash
64 | npm run build
65 | npm start
66 | ```
67 |
68 | ## Documentation
69 |
70 | You can find the full documentation of Vuido at [vuido.mimec.org](https://vuido.mimec.org/).
71 |
72 |
73 | ## Development status
74 |
75 | At the moment Vuido is in a very early stage of development. The main goal is to implement all remaining controls currently supported by libui.
76 |
77 | ## Acknowledgements
78 |
79 | Vuido is largely based on Vue.js and shares most of its code, except for the platform specific code related to libui.
80 |
81 | Vuido was inspired by [Proton Native](https://github.com/kusti8/proton-native), an environment for creating native desktop applications using React.
82 |
83 | ## License
84 |
85 | Vuido is licensed under the MIT license
86 |
87 | Copyright (C) 2018 Michał Męciński
88 |
--------------------------------------------------------------------------------
/src/runtime/elements/element.js:
--------------------------------------------------------------------------------
1 | import { TextNode } from '../nodes/textnode'
2 |
3 | export class Element {
4 | constructor( tagName ) {
5 | this.parentNode = null;
6 | this.prevSibling = null;
7 | this.nextSibling = null;
8 |
9 | this.tagName = tagName;
10 |
11 | this.childNodes = [];
12 |
13 | this.attributes = this._getDefaultAttributes();
14 | this.handlers = {};
15 | }
16 |
17 | appendChild( childNode ) {
18 | if ( childNode == null )
19 | throw new Error( 'Child node cannot be empty' );
20 |
21 | if ( childNode.parentNode != null )
22 | throw new Error( 'Child node already has a parent' );
23 |
24 | if ( childNode instanceof TextNode && this.childNodes.length > 0 )
25 | throw new Error( 'Element cannot contian multiple text nodes' );
26 |
27 | childNode.parentNode = this;
28 |
29 | if ( this.childNodes.length > 0 ) {
30 | const lastChild = this.childNodes[ this.childNodes.length - 1 ];
31 | childNode.prevSibling = lastChild;
32 | lastChild.nextSibling = childNode;
33 | }
34 |
35 | this.childNodes.push( childNode );
36 | }
37 |
38 | insertBefore( childNode, referenceNode ) {
39 | if ( childNode == null )
40 | throw new Error( 'Child node cannot be empty' );
41 |
42 | if ( referenceNode != null && referenceNode.parentNode != this )
43 | throw new Error( 'Reference node has invalid parent' );
44 |
45 | if ( childNode.parentNode != null )
46 | throw new Error( 'Child node already has a parent' );
47 |
48 | if ( childNode instanceof TextNode )
49 | throw new Error( 'Text node cannot be inserted dynamically' );
50 |
51 | const index = this.childNodes.indexOf( referenceNode );
52 |
53 | childNode.parentNode = this;
54 | childNode.nextSibling = referenceNode;
55 | childNode.prevSibling = this.childNodes[ index - 1 ];
56 |
57 | referenceNode.prevSibling = childNode;
58 | this.childNodes.splice( index, 0, childNode );
59 |
60 | return index;
61 | }
62 |
63 | removeChild( childNode ) {
64 | if ( childNode == null )
65 | throw new Error( 'Child node cannot be empty' );
66 |
67 | if ( childNode.parentNode != this )
68 | throw new Error( 'Child node has invalid parent' );
69 |
70 | if ( childNode instanceof TextNode )
71 | throw new Error( 'Text node cannot be removed dynamically' );
72 |
73 | childNode.parentNode = null;
74 |
75 | if ( childNode.prevSibling != null )
76 | childNode.prevSibling.nextSibling = childNode.nextSibling;
77 |
78 | if ( childNode.nextSibling != null )
79 | childNode.nextSibling.prevSibling = childNode.prevSibling;
80 |
81 | const index = this.childNodes.indexOf( childNode );
82 |
83 | this.childNodes.splice( index, 1 );
84 | }
85 |
86 | setAttribute( key, value ) {
87 | this.attributes[ key ] = value;
88 | }
89 |
90 | addEventListener( event, handler ) {
91 | this.handlers[ event ] = handler;
92 | }
93 |
94 | removeEventListener( event ) {
95 | delete this.handlers[ event ];
96 | }
97 |
98 | _getDefaultAttributes() {
99 | return {};
100 | }
101 |
102 | _setContentText( text ) {
103 | throw new Error( this.tagName + ' cannot contain text nodes' );
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/runtime/elements/widget.js:
--------------------------------------------------------------------------------
1 | import { Element } from './element'
2 | import { TextNode } from '../nodes/textnode'
3 |
4 | export class Widget extends Element {
5 | constructor( tagName ) {
6 | super( tagName );
7 |
8 | this.widget = null;
9 | this.widgetIndex = null;
10 |
11 | }
12 |
13 | appendChild( childNode ) {
14 | super.appendChild( childNode );
15 |
16 | if ( this.widget != null ) {
17 | if ( childNode instanceof Element )
18 | this._appendElement( childNode );
19 | else if ( childNode instanceof TextNode )
20 | this._setWidgetText( child.text );
21 | }
22 | }
23 |
24 | insertBefore( childNode, referenceNode ) {
25 | const index = super.insertBefore( childNode, referenceNode );
26 |
27 | if ( this.widget != null ) {
28 | if ( childNode instanceof Element )
29 | this._insertElement( childNode, index );
30 | }
31 |
32 | return index;
33 | }
34 |
35 | removeChild( childNode ) {
36 | super.removeChild( childNode );
37 |
38 | if ( this.widget != null ) {
39 | if ( childNode instanceof Element )
40 | this._removeElement( childNode );
41 | }
42 | }
43 |
44 | setAttribute( key, value ) {
45 | super.setAttribute( key, value );
46 |
47 | if ( this.widget != null )
48 | this._setWidgetAttribute( key, value );
49 | }
50 |
51 | addEventListener( event, handler ) {
52 | super.addEventListener( event, handler );
53 |
54 | if ( this.widget != null )
55 | this._setWidgetHandler( event, handler );
56 | }
57 |
58 | removeEventListener( event ) {
59 | super.removeEventListener( event );
60 |
61 | if ( this.widget != null )
62 | this._setWidgetHandler( event, null );
63 | }
64 |
65 | _mountWidget() {
66 | this._createWidget();
67 | this._initializeWidgetAttributes();
68 |
69 | for ( let key in this.handlers )
70 | this._setWidgetHandler( key, this.handlers[ key ] );
71 |
72 | for ( let i = 0; i < this.childNodes.length; i++ ) {
73 | const childNode = this.childNodes[ i ];
74 | if ( childNode instanceof Element )
75 | this._appendElement( childNode );
76 | else if ( childNode instanceof TextNode )
77 | this._setWidgetText( childNode.text );
78 | }
79 | }
80 |
81 | _getDefaultAttributes() {
82 | return {
83 | visible: true,
84 | enabled: true,
85 | stretchy: false
86 | };
87 | }
88 |
89 | _createWidget() {
90 | throw new Error( this.tagName + ' cannot be created' );
91 | }
92 |
93 | _destroyWidget() {
94 | this.widget.destroy();
95 | this.widget = null;
96 |
97 | this.childNodes = [];
98 | }
99 |
100 | _appendElement( childNode ) {
101 | if ( !( childNode instanceof Widget ) )
102 | throw new Error( this.tagName + ' cannot contain ' + childNode.tagName + ' elements' );
103 |
104 | childNode._mountWidget();
105 | this._appendWidget( childNode );
106 |
107 | childNode.widgetIndex = this.childNodes.indexOf( childNode );
108 | }
109 |
110 | _insertElement( childNode, index ) {
111 | if ( !( childNode instanceof Widget ) )
112 | throw new Error( this.tagName + ' cannot contain ' + childNode.tagName + ' elements' );
113 |
114 | for ( let i = this.childNodes.length - 1; i > index; i-- ) {
115 | const tailNode = this.childNodes[ i ];
116 | if ( tailNode instanceof Element )
117 | this._removeWidget( tailNode );
118 | }
119 |
120 | childNode._mountWidget();
121 | this._appendWidget( childNode );
122 |
123 | for ( let i = index + 1; i < this.childNodes.length; i++ ) {
124 | const tailNode = this.childNodes[ i ];
125 | if ( tailNode instanceof Element )
126 | this._appendWidget( tailNode );
127 | }
128 |
129 | this._reindexChildWidgets();
130 | }
131 |
132 | _removeElement( childNode ) {
133 | this._removeWidget( childNode );
134 |
135 | childNode._destroyWidget();
136 |
137 | this._reindexChildWidgets();
138 | }
139 |
140 | _appendWidget( childNode ) {
141 | throw new Error( this.tagName + ' cannot contain child widgets' );
142 | }
143 |
144 | _removeWidget( childNode ) {
145 | throw new Error( this.tagName + ' cannot contain child widgets' );
146 | }
147 |
148 | _setContentText( text ) {
149 | if ( this.widget != null )
150 | this._setWidgetText( text );
151 | }
152 |
153 | _setWidgetText( text ) {
154 | throw new Error( this.tagName + ' cannot contain text nodes' );
155 | }
156 |
157 | _initializeWidgetAttributes() {
158 | if ( !this.attributes.visible )
159 | this.widget.visible = false;
160 | if ( !this.attributes.enabled )
161 | this.widget.enabled = false;
162 | }
163 |
164 | _setWidgetAttribute( key, value ) {
165 | if ( key == 'visible' )
166 | this.widget.visible = value;
167 | else if ( key == 'enabled' )
168 | this.widget.enabled = value;
169 | else
170 | throw new Error( this.tagName + ' does not have attribute ' + key );
171 | }
172 |
173 | _setWidgetHandler( event, handler ) {
174 | throw new Error( this.tagName + ' does not have event ' + event );
175 | }
176 |
177 | _reindexChildWidgets() {
178 | let index = 0;
179 | for ( let i = 0; i < this.childNodes.length; i++ ) {
180 | const childNode = this.childNodes[ i ];
181 | if ( childNode instanceof Widget )
182 | childNode.widgetIndex = index++;
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------