Add initial documentation, add form, and prep for tab (pass full parent, not just element)

This commit is contained in:
kusti8 2018-02-05 19:43:25 -05:00
parent 6266d3c336
commit a35ed645ae
19 changed files with 284 additions and 45 deletions

10
Demo.js
View file

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Window, App, Text, render, Button, Box, TextInput, ColorButton } from './src/';
import { Window, App, Text, render, Button, Box, TextInput, ColorButton, Form } from './src/';
class A extends Component {
state = { name: true };
@ -8,16 +8,16 @@ class A extends Component {
return (
<App>
<Window name="Hi" height={640} width={480} menuBar={true}>
<Box vertical={this.state.name} padded={true}>
<Form padded={true}>
<Button
stretchy={false}
label="Don't press the button"
onClicked={() => this.setState({ name: !this.state.name })}
>
Hello
</Button>
<ColorButton stretchy={false} color='white' onChanged={color => console.log(color)}/>
<TextInput multiline={false} stretchy={false} secure={true} onChanged={text => console.log(text)}>My name is Gustav!</TextInput>
</Box>
<TextInput label="Very secret" multiline={false} stretchy={false} secure={true} onChanged={text => console.log(text)}>My name is Gustav!</TextInput>
</Form>
</Window>
</App>
);

0
docs/.nojekyll Normal file
View file

97
docs/README.md Normal file
View file

@ -0,0 +1,97 @@
# Reactor Native
> Create native desktop applications through a React syntax, on all platforms
## Why?
On mobile, it used to be hard to build beatiful cross-platform apps. Then React Native came along, giving us
a seamless way to build user interfaces and manage state in code, all while doing it cross platform.
On desktop, there is no such tool. You can create a GUI using something like Qt, but the code to make it is messy and unorganized.
Having made a very large GUI myself, it gets very cumbersome to manage all of that.
Some of you might be saying that you could do it in Electron. It's a good tool, but it brings in a lot of overhead, running a full webbrowser
to manage a small GUI, while Reactor Native can do the same, using native tools, with a smaller size and with less resource usage.
Reactor Native does the same
to desktop that React Native did to mobile. Build cross-platform apps for the desktop, all while never leaving the React eco-system. Popular
React packages such as Redux still work.
**Compare this code in Qt (Python):**
```python
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,
QPushButton, QApplication)
from PyQt5.QtGui import QFont
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def print_hello(self):
print("Hello")
def initUI(self):
btn = QPushButton('Button', self)
btn.clicked.connect(self.print_hello)
btn.resize(btn.sizeHint())
btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Example')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
```
**To this code using Reactor Native:**
```javascript
import React, { Component } from 'react';
import { render, Window, App, Button } from 'reactor-native';
class Example extends Component {
state = { name: true };
render() {
return (
<App>
<Window name="Example" height={300} width={300} menuBar={false}>
<Button
stretchy={false}
onClicked={() => console.log("Hello")}
>
Button
</Button>
</Window>
</App>
);
}
}
// This will create a file 'text.docx' in the current directory!
render(<Example />);
```
It is not only shorter, it is also easier to read and to edit, and can easily utilize the power of the state.
## Features
- Same syntax as React Native. Only a few component names have changed, almost everything else is the same
- Cross platform
- Native components. No more Electron
## Examples TODO ADD LINK
Check out the examples to see more.

2
docs/_sidebar.md Normal file
View file

@ -0,0 +1,2 @@
- [Home](/)
- [Quick start](quickstart.md)

22
docs/index.html Normal file
View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-native-cross - React Native for the desktop, cross compatible</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="React Native for the desktop, cross compatible">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/dark.css">
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'react-native-cross',
repo: 'https://github.com/kusti8/react-native-cross',
loadSidebar: true
}
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
</body>
</html>

24
docs/js_example.js Normal file
View file

@ -0,0 +1,24 @@
import React, { Component } from 'react';
import { render, Window, App, Button } from 'reactor-native';
class Example extends Component {
state = { name: true };
render() {
return (
<App>
<Window name="Example" height={300} width={300} menuBar={false}>
<Button
stretchy={false}
onClicked={() => console.log("Hello")}
>
Button
</Button>
</Window>
</App>
);
}
}
// This will create a file 'text.docx' in the current directory!
render(<Example />);

33
docs/python_example.py Normal file
View file

@ -0,0 +1,33 @@
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,
QPushButton, QApplication)
from PyQt5.QtGui import QFont
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def print_hello(self):
print("Hello")
def initUI(self):
btn = QPushButton('Button', self)
btn.clicked.connect(self.print_hello)
btn.resize(btn.sizeHint())
btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Example')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

2
docs/quickstart.md Normal file
View file

@ -0,0 +1,2 @@
# Quickstart

View file

@ -28,6 +28,7 @@
"fbjs": "^0.8.16",
"libui-node": "kusti8/libui-node",
"performance-now": "^2.1.0",
"prop-types": "^15.6.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-reconciler": "^0.7.0"

View file

@ -19,9 +19,9 @@ class ColorButton extends DesktopComponent {
input = input.toLowerCase()
let alpha
let c = Color(input).object()
if (typeof c.alpha !== 'undefined') {
if (this.exists(c.alpha)) {
alpha = c.alpha
} else if (typeof c.a !== 'undefined') {
} else if (this.exists(c.a)) {
alpha = c.a
} else {
alpha = 1
@ -38,7 +38,7 @@ class ColorButton extends DesktopComponent {
this.element.color = this.convertToColor(newProps.color);
}
if (typeof this.expectedEvents !== 'undefined') {
if (this.exists(this.expectedEvents)) {
for (let prop of this.expectedEvents) { // event props
if (newProps[prop] !== oldProps[prop]) {
this.element[prop](newProps[prop]);
@ -48,13 +48,13 @@ class ColorButton extends DesktopComponent {
}
initialProps(props) {
if (typeof props !== 'undefined') {
if (typeof props.color !== 'undefined') {
if (this.exists(props)) {
if (this.exists(props.color)) {
this.element.color = this.convertToColor(props.color)
}
}
if (typeof this.expectedEvents !== 'undefined') {
if (this.exists(this.expectedEvents)) {
for (let prop in this.expectedEvents) { // event props
if (prop in props) {
if (this.expectedEvents[prop] !== '') {
@ -66,7 +66,7 @@ class ColorButton extends DesktopComponent {
}
}
if (typeof this.expectedChild !== 'undefined') { // text child
if (this.exists(this.expectedChild)) { // text child
if (props.children) {
this.element[this.expectedChild] = props.children;
}

View file

@ -5,24 +5,27 @@ export default class DesktopComponent {
this.children = [];
}
appendChild(child) {
exists(a) {
return typeof a !== 'undefined'
}
appendChild(child) { // add a child to the list to be rendered
this.children.push(child);
}
removeChild(child) {
if (typeof this.element.setChild !== 'undefined') {
removeChild(child) { // remove it, and destroy it
if (this.exists(this.element.setChild)) {
// if it can only have one child, we don't need to "de-render" it
} else if (typeof this.element.deleteAt !== 'undefined') {
} else if (this.exists(this.element.deleteAt)) {
// if it can have multiple ex. VerticalBox
this.element.deleteAt(this.children.indexOf(child));
child.element.destroy();
}
const index = this.children.indexOf(child);
this.children.splice(index, 1);
console.log('Completed delete');
}
renderChildNode(parent) {
renderChildNode(parent) { // render the children
for (let i = 0; i < this.children.length; i += 1) {
if (typeof this.children[i] === 'object') {
this.children[i].render(parent);
@ -30,22 +33,31 @@ export default class DesktopComponent {
}
}
addParent(parent) {
if (typeof parent.setChild !== 'undefined') {
parent.setChild(this.element);
} else if (typeof parent.append !== 'undefined') {
if (typeof this.specialAppend !== 'undefined') { // we have special parameters for append
this.specialAppend(parent)
} else {
const stretchy =
typeof this.props.stretchy === 'undefined' ? true : this.props.stretchy;
parent.append(this.element, stretchy);
addParentAppend(parent) { // append to parent. Can be overriden
const stretchy =
!this.exists(this.props.stretchy) ? true : this.props.stretchy;
if (parent instanceof libui.UiForm) {
parent.element.append(this.props.label, this.element, stretchy)
} else if (parent instanceof libui.UiTab) {
parent.element.append(this.props.label, this.element, stretchy)
if (this.exists(this.props.margined)) {
//parent.element.setMargined()
}
} else {
parent.element.append(this.element, stretchy);
}
}
addParent(parent) { // add itself to the parent
if (this.exists(parent.element.setChild)) {
parent.element.setChild(this.element);
} else if (this.exists(parent.element.append)) {
this.addParentAppend(parent) // append itself to the parent
}
}
updateProps(oldProps, newProps) {
if (typeof this.expectedProps !== 'undefined') {
if (this.exists(this.expectedProps)) {
for (let prop of this.expectedProps) { // normal props
if (newProps[prop] !== oldProps[prop] && prop in newProps) {
this.element[prop] = newProps[prop];
@ -55,7 +67,7 @@ export default class DesktopComponent {
}
updateEvents(oldProps, newProps) {
if (typeof this.expectedEvents !== 'undefined') {
if (this.exists(this.expectedEvents)) {
for (let prop in this.expectedEvents) { // event props
if (prop in newProps && newProps[prop] !== oldProps[prop]) {
if (this.expectedEvents[prop] !== '') {
@ -69,22 +81,22 @@ export default class DesktopComponent {
}
updateChild(oldProps, newProps) {
if (typeof this.expectedChild !== 'undefined') {
if (this.exists(this.expectedChild)) {
if (newProps.children !== oldProps.children) { // text child
this.element[this.expectedChild] = newProps.children;
}
}
}
update(oldProps, newProps) {
update(oldProps, newProps) { // update all things, split into props, events, and children
this.updateProps(oldProps, newProps)
this.updateEvents(oldProps, newProps)
this.updateChild(oldProps, newProps)
}
initialNormalProps(props) {
if (typeof props !== 'undefined') {
if (typeof this.expectedProps !== 'undefined') {
if (this.exists(props)) {
if (this.exists(this.expectedProps)) {
for (let prop of this.expectedProps) { // normal props
if (prop in props) {
this.element[prop] = props[prop];
@ -95,7 +107,7 @@ export default class DesktopComponent {
}
initialEvents(props) {
if (typeof this.expectedEvents !== 'undefined') {
if (this.exists(this.expectedEvents)) {
for (let prop in this.expectedEvents) { // event props
if (prop in props) {
if (this.expectedEvents[prop] !== '') {
@ -109,14 +121,14 @@ export default class DesktopComponent {
}
initialChild(props) {
if (typeof this.expectedChild !== 'undefined') { // text child
if (this.exists(this.expectedChild)) { // text child
if (props.children) {
this.element[this.expectedChild] = props.children;
}
}
}
initialProps(props) {
initialProps(props) { // same, but don't check for oldProps vs newProps, just set them
this.initialNormalProps(props)
this.initialEvents(props)
this.initialChild(props)

View file

@ -0,0 +1,21 @@
import DesktopComponent from './DesktopComponent';
import libui from 'libui-node';
class Form extends DesktopComponent {
expectedProps = ['enabled', 'visible', 'padded']
constructor(root, props) {
super(root, props);
this.root = root;
this.props = props;
this.element = new libui.UiForm();
this.initialProps(props)
}
render(parent) {
this.addParent(parent)
this.renderChildNode(this);
}
}
export default Form;

View file

@ -14,7 +14,7 @@ class HorizontalBox extends DesktopComponent {
render(parent) {
this.addParent(parent)
this.renderChildNode(this.element);
this.renderChildNode(this);
}
}

21
src/components/Tab.js Normal file
View file

@ -0,0 +1,21 @@
import DesktopComponent from './DesktopComponent';
import libui from 'libui-node';
class Tab extends DesktopComponent {
expectedProps = ['enabled', 'visible']
constructor(root, props) {
super(root, props);
this.root = root;
this.props = props;
this.element = new libui.UiTab();
this.initialProps(props)
}
render(parent) {
this.addParent(parent)
this.renderChildNode(this);
}
}
export default Tab;

View file

@ -14,7 +14,7 @@ class VerticalBox extends DesktopComponent {
render(parent) {
this.addParent(parent)
this.renderChildNode(this.element);
this.renderChildNode(this);
}
}

View file

@ -13,14 +13,14 @@ class Window extends DesktopComponent {
props.menuBar
);
this.element.onClosing(() => {
if (typeof this.props.onClose !== 'undefined') {
if (this.exists(this.props.onClose)) {
this.props.onClose();
}
this.element.close();
console.log(this.props.lastWindow);
if (
this.props.lastWindow === true ||
typeof this.props.lastWindow === 'undefined'
!this.exists(this.props.lastWindow)
) {
libui.stopLoop();
}
@ -41,7 +41,7 @@ class Window extends DesktopComponent {
render() {
this.element.show();
this.renderChildNode(this.element);
this.renderChildNode(this);
}
}

View file

@ -9,5 +9,6 @@ import Entry from './Entry'
import PasswordEntry from './PasswordEntry'
import MultilineEntry from './MultilineEntry'
import ColorButton from './ColorButton'
import Form from './Form'
export { Root, Text, App, Window, Button, VerticalBox, HorizontalBox, Entry, PasswordEntry, MultilineEntry, ColorButton };
export { Root, Text, App, Window, Button, VerticalBox, HorizontalBox, Entry, PasswordEntry, MultilineEntry, ColorButton, Form };

View file

@ -12,5 +12,6 @@ const Entry = 'ENTRY'
const PasswordEntry = 'PASSWORDENTRY'
const MultilineEntry = 'MULTILINEENTRY'
const ColorButton = 'COLORBUTTON'
const Form = 'FORM'
export { render, Text, App, Window, Button, VerticalBox, HorizontalBox, Box, Entry, PasswordEntry, TextInput, MultilineEntry, ColorButton };
export { render, Text, App, Window, Button, VerticalBox, HorizontalBox, Box, Entry, PasswordEntry, TextInput, MultilineEntry, ColorButton, Form };

View file

@ -9,7 +9,8 @@ import {
Entry,
PasswordEntry,
MultilineEntry,
ColorButton
ColorButton,
Form
} from '../components/';
import { ROOT_NODE } from '../render/';
@ -39,6 +40,7 @@ function createElement(type, props) {
PASSWORDENTRY: () => new PasswordEntry(ROOT_NODE, props),
MULTILINEENTRY: () => new MultilineEntry(ROOT_NODE, props),
COLORBUTTON: () => new ColorButton(ROOT_NODE, props),
FORM: () => new Form(ROOT_NODE, props),
default: undefined,
};