├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── ROADMAP.md
├── code-of-conduct.md
├── docs
├── Alignment.html
├── App.js.html
├── BasePolicy.html
├── BaseWidget.html
├── BoxLayout.html
├── Copyable.html
├── ExpandingPolicy.html
├── FixedLayout.html
├── FixedPolicy.html
├── GraphicsGen.html
├── GraphicsGen.js.html
├── HBoxLayout.html
├── LayoutManager.html
├── Loader.html
├── Padding.html
├── Padding.js.html
├── Point.html
├── Point.js.html
├── ST.Alignment.html
├── ST.App.html
├── ST.GraphicsGen.html
├── ST.Layouts.BaseLayout.html
├── ST.Layouts.BoxLayout.html
├── ST.Layouts.FixedLayout.html
├── ST.Layouts.HBoxLayout.html
├── ST.Layouts.VBoxLayout.html
├── ST.Padding.html
├── ST.Point.html
├── ST.Settings.html
├── ST.Size.html
├── ST.SizePolicies.BasePolicy.html
├── ST.SizePolicies.ExpandingPolicy.html
├── ST.SizePolicies.FixedPolicy.html
├── ST.SizePolicies.SharedExpandingPolicy.html
├── ST.Theme.html
├── ST.Widgets.BaseWidget.html
├── ST.Widgets.Button.html
├── ST.Widgets.Container.html
├── ST.Widgets.Image.html
├── ST.Widgets.Label.html
├── ST.Widgets.Panel.html
├── ST.Widgets.SITransform.html
├── ST.Widgets.Slider.html
├── ST.Widgets.StageWidget.html
├── ST.Widgets.TextButton.html
├── Settings.js.html
├── SharedExpandingPolicy.html
├── Signal.html
├── Size.html
├── Size.js.html
├── SizeManager.html
├── Slot.html
├── Theme.html
├── Theme.js.html
├── UI.Alignment.Alignment.html
├── UI.Alignment.html
├── UI.App.html
├── UI.BaseLayout.html
├── UI.Copyable.html
├── UI.GraphicsGen.html
├── UI.Label.html
├── UI.LayoutManager.html
├── UI.Layouts.BaseLayout.html
├── UI.Layouts.BoxLayout.html
├── UI.Layouts.FixedLayout.html
├── UI.Layouts.HBoxLayout.html
├── UI.Layouts.VBoxLayout.html
├── UI.Layouts.exports.HBoxLayout.html
├── UI.Layouts.exports.VBoxLayout.html
├── UI.Padding.html
├── UI.Point.html
├── UI.Renderer.html
├── UI.Signal.html
├── UI.Size.html
├── UI.SizeManager.html
├── UI.SizePolicies.BasePolicy.html
├── UI.SizePolicies.ExpandingPolicy.html
├── UI.SizePolicies.FixedPolicy.html
├── UI.SizePolicies.SharedExpandingPolicy.html
├── UI.Slot.html
├── UI.Theme.html
├── UI.UIGraphics.html
├── UI.WidgetShader.html
├── UI.Widgets.BaseWidget.html
├── UI.Widgets.Button.html
├── UI.Widgets.Container.html
├── UI.Widgets.Image.html
├── UI.Widgets.Label.html
├── UI.Widgets.Panel.html
├── UI.Widgets.SITransform.html
├── UI.Widgets.Slider.html
├── UI.Widgets.StageWidget.html
├── UI.Widgets.TextButton.html
├── UI.module.exports.html
├── UIGraphics.html
├── UIGraphics.js.html
├── VBoxLayout.html
├── _config.yml
├── classes.list.html
├── const.js.html
├── drawcount.js.html
├── external-Container.html
├── external-EventEmitter.html
├── external-PIXI.html
├── external-TransformStatic.html
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
├── global.html
├── img
│ ├── glyphicons-halflings-white.png
│ └── glyphicons-halflings.png
├── index.html
├── index.js.html
├── layoutSys_Alignment.js.html
├── layoutSys_layouts_BaseLayout.js.html
├── layoutSys_layouts_BoxLayout.js.html
├── layoutSys_layouts_FixedLayout.js.html
├── layoutSys_layouts_HBoxLayout.js.html
├── layoutSys_layouts_VBoxLayout.js.html
├── layoutSys_sizePolicies_BasePolicy.js.html
├── layoutSys_sizePolicies_ExpandingPolicy.js.html
├── layoutSys_sizePolicies_FixedPolicy.js.html
├── layoutSys_sizePolicies_SharedExpandingPolicy.js.html
├── module.exports.html
├── options.js.html
├── quicksearch.html
├── scripts
│ ├── bootstrap.min.js
│ ├── docstrap.lib.js
│ ├── fulltext-search-ui.js
│ ├── fulltext-search.js
│ ├── jaguar.js
│ ├── jquery.min.js
│ ├── jquery.min.map
│ ├── lunr.min.js
│ ├── prettify
│ │ ├── Apache-License-2.0.txt
│ │ ├── jquery.min.js
│ │ ├── lang-css.js
│ │ └── prettify.js
│ ├── sunlight.js
│ ├── toc.js
│ ├── underscore-min.js
│ └── underscore-min.map
├── styles
│ ├── bootstrap.min.css
│ ├── darkstrap.css
│ ├── jaguar.css
│ ├── prettify-jsdoc.css
│ ├── prettify-tomorrow.css
│ ├── site.cerulean.css
│ ├── site.cosmo.css
│ ├── site.cyborg.css
│ ├── site.darkly.css
│ ├── site.darkstrap.css
│ ├── site.dibs-bootstrap.css
│ ├── site.flatly.css
│ ├── site.journal.css
│ ├── site.lumen.css
│ ├── site.paper.css
│ ├── site.readable.css
│ ├── site.sandstone.css
│ ├── site.simplex.css
│ ├── site.slate.css
│ ├── site.spacelab.css
│ ├── site.superhero.css
│ ├── site.united.css
│ ├── site.yeti.css
│ ├── sunlight.dark.css
│ └── sunlight.default.css
├── widgets_BaseWidget.js.html
├── widgets_Button.js.html
├── widgets_Container.js.html
├── widgets_Image.js.html
├── widgets_Label.js.html
├── widgets_Panel.js.html
├── widgets_SITransform.js.html
├── widgets_Slider.js.html
├── widgets_StageWidget.js.html
└── widgets_TextButton.js.html
├── gulpfile.js
├── inch.json
├── jsdoc.json
├── package.json
├── screenshot.jpg
├── scripts
└── fixJsdoc.js
├── src
├── App.js
├── GraphicsGen.js
├── Padding.js
├── Point.js
├── Settings.js
├── Size.js
├── Theme.js
├── const.js
├── demos
│ ├── demos.js
│ └── index.html
├── drawcount.js
├── index.js
├── layoutSys
│ ├── Alignment.js
│ ├── index.js
│ ├── layouts
│ │ ├── BaseLayout.js
│ │ ├── BoxLayout.js
│ │ ├── FixedLayout.js
│ │ ├── HBoxLayout.js
│ │ ├── VBoxLayout.js
│ │ └── index.js
│ └── sizePolicies
│ │ ├── BasePolicy.js
│ │ ├── ExpandingPolicy.js
│ │ ├── FixedPolicy.js
│ │ ├── SharedExpandingPolicy.js
│ │ └── index.js
└── widgets
│ ├── BaseWidget.js
│ ├── Button.js
│ ├── Container.js
│ ├── Image.js
│ ├── Label.js
│ ├── Panel.js
│ ├── SITransform.js
│ ├── Slider.js
│ ├── StageWidget.js
│ ├── TextButton.js
│ └── index.js
├── styleSheets
├── greyToadTheme.js
└── holyStyle.js
└── test
├── index.js
├── runner.html
├── spec
├── App-spec.js
├── GraphicsGen-spec.js
├── Padding-spec.js
├── Point-spec.js
├── Settings-spec.js
├── Size-spec.js
├── Theme-spec.js
├── index.js
├── layoutSys
│ ├── Alignment-spec.js
│ ├── index.js
│ ├── layouts
│ │ ├── BaseLayout-spec.js
│ │ ├── BoxLayout-spec.js
│ │ ├── FixedLayout-spec.js
│ │ ├── HBoxLayout-spec.js
│ │ ├── VBoxLayout-spec.js
│ │ └── index.js
│ └── sizePolicies
│ │ ├── BasePolicy-spec.js
│ │ ├── ExpandingPolicy-spec.js
│ │ ├── FixedPolicy-spec.js
│ │ ├── SharedExpandingPolicy-spec.js
│ │ └── index.js
└── widgets
│ ├── BaseWidget-spec.js
│ ├── Button-spec.js
│ ├── Image-spec.js
│ ├── Label-spec.js
│ ├── Panel-spec.js
│ ├── SITransform-spec.js
│ ├── Slider-spec.js
│ ├── StageWidget-spec.js
│ ├── TextButton-spec.js
│ └── index.js
└── test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "es2015" ],
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://EditorConfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | indent_style = space
10 | indent_size = 4
11 |
12 | [{package.json,bower.json,.travis.yml}]
13 |
14 | indent_size = 2
15 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "parserOptions": {
8 | "ecmaVersion": 6,
9 | "sourceType": "module",
10 | "ecmaFeatures": {
11 |
12 | }
13 | },
14 | "extends": "google",
15 | "rules": {
16 | "linebreak-style": 0
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Lib dir used for npm
40 | lib/
41 |
42 | # other
43 | www/
44 |
45 | # Dist folder
46 | dist/
47 |
48 | # Typescript v1 declaration files
49 | typings/
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional REPL history
58 | .node_repl_history
59 |
60 | # Output of 'npm pack'
61 | *.tgz
62 |
63 | # Yarn Integrity file
64 | .yarn-integrity
65 |
66 | # dotenv environment variables file
67 | .env
68 |
69 | # release protocol
70 | ReleaseProtocol.txt
71 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # ignore src folder (use lib)
2 | src/
3 |
4 | # other files
5 | .gitignore
6 | .gitattributes
7 | .travis.yml
8 | gulpfile.js
9 | inch.json
10 | jsdoc.json
11 |
12 | # ignore test
13 | test/
14 |
15 | # ignore docs
16 | docs/
17 |
18 | # ignore dist
19 | dist/
20 |
21 | # ignore scripts
22 | scripts/
23 |
24 | # other
25 | www/
26 |
27 | # Logs
28 | logs
29 | *.log
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 |
34 | # Dependency directories
35 | node_modules/
36 | jspm_packages/
37 |
38 | # Typescript v1 declaration files
39 | typings/
40 |
41 | # Optional npm cache directory
42 | .npm
43 |
44 | # Optional eslint cache
45 | .eslintcache
46 |
47 | # Optional REPL history
48 | .node_repl_history
49 |
50 | # Output of 'npm pack'
51 | *.tgz
52 |
53 | # Yarn Integrity file
54 | .yarn-integrity
55 |
56 | # dotenv environment variables file
57 | .env
58 |
59 | # release protocol
60 | ReleaseProtocol.txt
61 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 |
5 | install:
6 | - npm install
7 | - npm install pixi-gl-core -save
8 | - npm install bit-twiddle
9 |
10 | cache:
11 | directories:
12 | - node_modules
13 |
14 | before_script:
15 | - npm install -g gulp
16 |
17 | script:
18 | - gulp
19 | - gulp test
20 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Sabertooth
2 |
3 | ### You're awesome!:blush:
4 |
5 | Thank you for considering contributing to Sabertooth. Big commitments aren't required. Even small things like grammatical errors in the [docs](https://abydosdigital.github.io/Sabertooth/) really add up. Sabertooth is in its infancy and will need your help to reach its potential. Every contribution is valued and greatly appreciated.
6 |
7 | >Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
8 |
9 | > Sabertooth is an open source project and we love to receive contributions from our community — you! There are numerous ways to contribute, from writing tutorials or blog posts, improving the [documentation](https://abydosdigital.github.io/Sabertooth/), submitting bug reports and feature requests or writing code which can be incorporated into Sabertooth itself.
10 |
11 | See the [ROADMAP](ROADMAP.md) to view the current state of features and to see some of the things we're trying to accomplish.
12 |
13 | ### Code of Conduct
14 | Please note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.
15 |
16 | ### Pull Request
17 |
18 | > When woking on large features or bug fixes it's best to split the work(break it down) into different branches/ pull request. This simplifies the process of reviewing and merging.
19 |
20 | Requirements for acceptance:
21 | * If it solves an issue it must reference the issue in the title
22 | * New features or methods must have at least basic corresponding test.
23 | * New classes and methods must have documentation(JSDoc comments).
24 | * It must match the coding style of the rest of the project and must pass the linter.
25 | * It must pass the travis-ci build and test.
26 |
27 | > PR's should be merged with the dev branch unless otherwise stated.
28 |
29 | # Your First Contribution
30 | No worries, we'd love to help you!:thumbsup:
31 |
32 | > Unsure where to begin contributing to Sabertooth? You can start by looking through the issue tracker for something simple or maybe read through the [docs](https://abydosdigital.github.io/Sabertooth/) to see if anything needs improvement.
33 |
34 | If you're new to Github check out these friendly tutorials:
35 | * [makeapullrequest.com](http://makeapullrequest.com/)
36 | * [firsttimersonly.com](http://www.firsttimersonly.com/)
37 | * [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)
38 |
39 | At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat:
40 |
41 | If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge.
42 |
43 | # Getting started
44 |
45 | ### Follow these instructions and you'll be on your way in no time.:smile:
46 |
47 | 1. Create your own fork of the code
48 | 2. Create a new branch for the fix or feature
49 | 3. Do the changes in your fork
50 | 4. If you like the change and think the project could use it:
51 | * Be sure you have followed the code style for the project.
52 | * Send a pull request to the dev branch describing the changes you've made.
53 |
54 | ### Large contributions
55 |
56 | Big changes need to fit with the direction of the project. There could be potential issues you didn't think of or maybe someone is already working on that particular feature. It's probably best to make an issue for your proposal first so that we can determine the best way to proceed.
57 |
58 | ### Small contributions
59 |
60 | As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following:
61 | * Spelling / grammar fixes
62 | * Typo correction, white space and formatting changes
63 | * Comment clean up
64 | * Bug fixes that change default return values or error codes stored in constants
65 | * Adding logging messages or debugging output
66 | * Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
67 | * Moving source files from one directory or package to another
68 |
69 | # How to report a bug
70 |
71 | When filing an issue, make sure to answer these five questions:
72 |
73 | 1. What version of Sabertooth are you using?
74 | 2. What operating system and processor architecture are you using?
75 | 3. What did you do?
76 | 4. What did you expect to see?
77 | 5. What did you see instead?
78 |
79 | # How to suggest a feature or enhancement
80 |
81 | ### Features
82 |
83 | >Feature request should only be opened for things that add new functionality to the project.
84 | To suggest a feature, open an issue and try to explain thoroughly why it should be added and how it should work. Please use the feature request label.
85 |
86 | ### Enhancements
87 |
88 | >Only suggest enhancements for things that improve upon existing functionality. To propose an enhancement, open an issue and try to explain thoroughly why it should be added and how it should work. Please use the enhancement label.
89 |
90 | # Questions
91 |
92 | Feel free to direct any further questions to AbydosDigital@gmail.com.
93 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2017, Zach Moore
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 | PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This was a fun project to build and experiment with. **It is not to be taken seriously.** A QT style UI in JS and WebGL would be too slow and bulky.
2 |
3 | My goal was to learn about QT's layout system, create a JS framework from scratch and setup continuous integration and automated testing with travis-ci.
4 |
5 | ---
6 |
7 | ## Sabertooth
8 |
9 | ### A hardware accelerated UI framework for JavaScript.
10 | Sabertooth is inspired by [QT](https://www.qt.io/), uses super-fast [PIXI.js](https://github.com/pixijs/pixi.js) under-the-hood and renders in webGL with canvas fallback. Widgets are extensions of PIXI.Container and have most of those properties, such as tint, alpha, blending modes and filters(shaders). Custom widgets and custom styling are easily accomplished by sub-classing existing widgets and modifying or creating new style sheets.
11 |
12 | * [Documentation](https://abydosdigital.github.io/Sabertooth/)
13 |
14 | (travis ci fails because the project has not been maintained and dependencies are out of date)
15 | [](https://travis-ci.org/AbydosDigital/Sabertooth)
16 | [](http://inch-ci.org/github/AbydosDigital/SaberTooth)
17 |
18 | 
19 |
20 | ***
21 |
23 |
24 | ### Get Started
25 |
26 | #### The easiest way to get started with Sabertooth is to [download](https://github.com/AbydosDigital/Sabertooth/releases) the latest release.
27 |
29 |
30 | **Example:**
31 | ```html
32 |
33 |
57 | ```
58 |
59 | #### Install with npm:
60 | ```
61 | $> npm install Sabertooth
62 | ```
63 | **Example:**
64 | ```javascript
65 | import * as ST from 'sabertooth'; // for es6 modules
66 |
67 | let app = new ST.App(/*options*/);
68 |
69 | let tb = new ST.Widgets.TextButton(app.root, {
70 | text: 'My Button',
71 | x: 100,
72 | y: 100,
73 | width: 200,
74 | height: 30,
75 | });
76 |
77 | // Set a click callback
78 | tb.on('click', ()=>{
79 | console.log('My Button Was Clicked!');
80 | });
81 |
82 | //main loop
83 | let main = function () {
84 | app.update(); // Update UI logic
85 | requestAnimationFrame(main); // Loop
86 | }
87 |
88 | main(); // Begin the main application loop
89 | ```
90 |
91 | #### Test
92 | ```
93 | gulp test
94 | ```
95 |
96 | #### To build locally:
97 | * Fork Sabertooth
98 | * Clone
99 | * CD to the project directory
100 | * Install dependencies:
101 | ```
102 | npm install
103 | ```
104 | Build:
105 | ```
106 | gulp
107 | ```
108 |
109 | ### Contributing
110 |
111 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
112 |
113 | ### Versioning
114 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/AbydosDigital/SaberTooth/tags).
115 |
116 | ### Authors
117 | * **Zach Moore** *- initial work -* [@Zachacious](https://github.com/Zachacious)
118 |
119 | See also the list of [contributors](https://github.com/AbydosDigital/Sabertooth/graphs/contributors) who participated in this project.
120 |
121 | ### License
122 | This project is licensed under the **ISC** license. See [LICENSE.md](LICENSE) for more details.
123 |
124 | ### Acknowledgements
125 | Special thanks to:
126 | * [GoodBoyDigital](http://www.goodboydigital.com/) - [Pixi.js](http://www.pixijs.com/)
127 | * [Primus](https://github.com/primus) - [EventEmitter3](https://github.com/primus/eventemitter3)
128 |
129 | ### Questions?
130 |
131 | Please contact AbydosDigital@gmail.com with any further questions.
132 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Sabertooth Project Roadmap
2 | This is meant to be a rough outline of where we're at and the journey ahead. Feel free to add your own ideas or updates:smile:
3 |
4 | Follow [@Zachacious](https://twitter.com/Zachacious) on twitter to stay up-to-date on the latest.
5 |
6 | ~ *All of the below is subject to change because of natural project evolution and depending on who's willing to work on what and when* ~
7 |
8 | **v0.3.0**
9 | * Make UI's skinnable
10 | * Bitmap labels
11 |
12 | **v0.4.0**
13 | * Keyboard input system
14 | * Scrollable viewports
15 |
16 | **v0.5.0**
17 | * List View
18 | * Tree View
19 |
20 | **v0.6.0**
21 | * Progress bar
22 | * Spring
23 | * Divider
24 | * Checkbox
25 | * RadioButton
26 |
27 | **v0.7.0**
28 | * TabView
29 | * Toolbars
30 |
31 | **v0.8.0**
32 | * Menus
33 |
34 | **v0.9.0**
35 | * Text input
36 |
37 | **v1.0.0:**
38 | * Simplify api names -- ST.Widgets.Button = ST.Button
39 |
40 | **v1.1.0**
41 | * TitleBar
42 | * Windows
43 |
44 | **v1.2.0**
45 | * Undo
46 |
47 | **v1.3.0**
48 | * Actions
49 |
50 | **v1.4.0**
51 | * Full working demos for mobile(Cocoonjs), web, and desktop(Electron)
52 |
53 | **v2.0.0**
54 | * Animations
55 | * Designer App
56 |
57 | ---
58 | ### Full list of features
59 |
60 | :white_circle: = Not implemented |
61 | :white_check_mark: = Already implimented
62 |
63 | * App:white_check_mark: - Handles creating the PIXI renderer, the root widget and automatic resizing
64 | * Theme:white_check_mark: - Creates themes from style sheets stored in js files
65 | * Skin::white_circle: - Use images and bitmap fonts instead of 'Flat Style' rectangles for theme.
66 | * Layouts
67 | * BaseLayout:white_check_mark:
68 | * FixedLayout:white_check_mark: - Arrange widgets via user defined positions
69 | * BoxLayout:white_check_mark: - Stack widgets vertically or horizontal
70 | * GridLayout:white_circle: - Arrange widgets in a grid
71 | * Size Policies
72 | * BasePolicy:white_check_mark:
73 | * FixedPolicy:white_check_mark: - Use user defined width or height
74 | * ExpandingPolicy:white_check_mark: - Fill the parent widget
75 | * SharedExpandingPolicy:white_check_mark: - Share space with siblings - fill the parent
76 | * CompressPolicy:white_circle: - Shrink to fit children contained within
77 | * Widgets
78 | * BaseWidget:white_check_mark:
79 | * Label:white_check_mark: - Show text
80 | * BitmapLabel:white_circle: - Show text using a bitmap font
81 | * Button:white_check_mark:
82 | * TextButton:white_check_mark: - Button with text
83 | * Image:white_check_mark: - Sprite Texture with widget properties
84 | * Panel:white_check_mark: - Container with solid background
85 | * Slider:white_check_mark: - Track with a button that slides to change values
86 | * Container:white_check_mark: - Non-renderable container of widgets
87 | * StageWidget( root widget ):white_check_mark: - Counts its own size with getBounds() - use as root widget
88 | * ProgressBar:white_circle: - Bar that fills over time
89 | * ScrollView:white_circle: - Content that moves with scrollbars
90 | * ListView:white_circle:
91 | * TreeView:white_circle:
92 | * Spring:white_circle: - Use to compress widgets in certain layouts
93 | * Divider:white_circle: - Simple bar use to divide space
94 | * TabView:white_circle:
95 | * Splitter:white_circle: - Adjustable bar that splits content areas
96 | * Window:white_circle: - Free moving windows
97 | * TitleBar( for windows ):white_circle:
98 | * Menu:white_circle: - Traditional and responsive menus of all kinds
99 | * Toolbar:white_circle:
100 | * ToolButton:white_circle:
101 | * MenuButton:white_circle:
102 | * Checkbox:white_circle:
103 | * RadioButton:white_circle:
104 | * TextInput:white_circle:
105 | * MultiLineTextInput:white_circle:
106 | * Alignments( for layouts ):white_check_mark: - Provides an offset for layout positioning
107 | * Settings:white_check_mark: - Configurable global settings for Sabertooth classes to use
108 | * Padding( for widgets ):white_check_mark:
109 | * Actions( QT style ):white_circle: - See [QT Actions](http://doc.qt.io/qt-5/qaction.html)
110 | * Undo:white_circle: - See [QT Undo](http://doc.qt.io/qt-4.8/qundo.html)
111 | * MVC:white_circle: - Use 'Item?' sub-classes interchangeably with different views
112 | * Keyboard Input:white_circle:
113 | * Designer App:white_circle: - Design and create widget layouts visually
114 | * Demo Project(s):white_circle: - To test desktop and mobile fully
115 | * Animations:white_circle: - For transitions
116 |
--------------------------------------------------------------------------------
/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at AbydosDigital@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/docs/HBoxLayout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ');
152 |
153 | headings.each(function(i, heading) {
154 | var $h = $(heading);
155 |
156 | var anchor = $('
').attr('id', opts.anchorName(i, heading, opts.prefix) + ANCHOR_PREFIX).insertBefore($h);
157 |
158 | var span = $('
')
159 | .text(opts.headerText(i, heading, $h));
160 |
161 | //build TOC item
162 | var a = $('
')
163 | .append(span)
164 | .attr('href', '#' + opts.anchorName(i, heading, opts.prefix))
165 | .bind('click', function(e) {
166 | scrollTo(e);
167 | el.trigger('selected', $(this).attr('href'));
168 | });
169 |
170 | span.addClass(opts.itemClass(i, heading, $h, opts.prefix));
171 |
172 | tocs.push(a);
173 |
174 | ul.append(a);
175 | });
176 | el.html(ul);
177 |
178 | calcHadingOffsets();
179 | });
180 | };
181 |
182 |
183 | jQuery.fn.toc.defaults = {
184 | container: 'body',
185 | selectors: 'h1,h2,h3',
186 | smoothScrolling: true,
187 | prefix: 'toc',
188 | onHighlight: function() {},
189 | highlightOnScroll: true,
190 | navbarOffset: 0,
191 | anchorName: function(i, heading, prefix) {
192 | return prefix+i;
193 | },
194 | headerText: function(i, heading, $heading) {
195 | return $heading.text();
196 | },
197 | itemClass: function(i, heading, $heading, prefix) {
198 | return prefix + '-' + $heading[0].tagName.toLowerCase();
199 | }
200 |
201 | };
202 |
203 | })(jQuery);
204 |
--------------------------------------------------------------------------------
/docs/styles/jaguar.css:
--------------------------------------------------------------------------------
1 | body,html{font:1em jaf-bernino-sans,"Lucida Grande","Lucida Sans Unicode","Lucida Sans",Geneva,Verdana,sans-serif;background-color:#fff}ol,ul{margin:0;padding:0}li{list-style-type:none}#wrap{position:relative}::-webkit-scrollbar{width:8px;background-color:transparent}::-webkit-scrollbar-thumb{background-color:gray;border-radius:4px}.navigation{position:fixed;float:left;width:250px;height:100%;background-color:#1a1a1a}.navigation .applicationName{margin:0;margin-top:15px;padding:10px 15px;font:700 1.25em Helvetica;color:#fff}.navigation .applicationName a{color:#fff}.navigation .search{padding:10px 15px}.navigation .search input{background-color:#333;color:#fff;border-color:#555}.navigation .list{padding:10px 15px 0 15px;position:relative;overflow:auto;width:100%}.navigation li.item{margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid #333}.navigation li.item a{color:#bbb}.navigation li.item a:hover{color:#fff}.navigation li.item .title{cursor:pointer;position:relative;display:block;font-size:.8em}.navigation li.item .title a{color:#e1e1e1}.navigation li.item .title a:hover{color:#fff}.navigation li.item .title .static{display:block;border-radius:3px;background-color:#779c34;color:#000;font-size:.7em;padding:2px 4px;float:right}.navigation li.item .subtitle{margin-top:10px;font:700 .65em Helvetica;color:#779c34;display:block}.navigation li.item ul>li{font-size:.7em;padding-left:8px;margin-top:2px}.navigation li.item .itemMembers{display:none}.main{padding:20px 20px;margin-left:250px}.main .page-title{display:none}.main h1{font-weight:700;font-size:1.6em;margin:0}.main h2{font-weight:700;font-size:1.5em;margin:0}.main h3{font-weight:700;font-size:12px;margin:5px 0}.main h4{font-weight:700;font-size:1em}.main h5{font-weight:700;font-size:12px}.main dd{font-size:12px}.main h4.name span.type-signature{display:inline-block;border-radius:3px;background-color:gray;color:#fff;font-size:.7em;padding:2px 4px}.main h4.name span.type{margin-left:5px}.main h4.name span.glyphicon{display:inline-block;vertical-align:middle;color:#e1e1e1;margin-left:7px}.main h4.name span.returnType{margin-left:3px;background-color:transparent!important;color:gray!important}.main span.static{display:inline-block;border-radius:3px;background-color:#779c34!important;color:#fff;font-size:.7em;padding:2px 4px;margin-right:8px}.main span.number{background-color:gray!important}.main span.string{background-color:gray!important}.main span.object{background-color:#2a6496!important}.main span.array{background-color:#2a6496!important}.main span.boolean{background-color:#ee7d7d!important}.main .subsection-title{font-size:14px;margin-top:30px;color:#779c34}.main .description{margin-top:10px;font-size:13px}.main .description ol,.main .description ul{margin-bottom:15px}.main .description p{font-size:13px}.main .description h2{margin-top:30px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #efefef}.main .description pre{margin:10px 0}.main .tag-source{font-size:12px}.main dt.tag-source{margin-top:5px}.main dt.tag-todo{font-size:10px;display:inline-block;background-color:#2a6496;color:#fff;padding:2px 4px;border-radius:5px}.main .type-signature{font-size:12px}.main .tag-deprecated{display:inline-block;font-size:10px}.main .important{background-color:#ee7d7d;color:#fff;padding:2px 4px;border-radius:5px}.main .nameContainer{position:relative;margin-top:20px;padding-top:5px;border-top:1px solid #e1e1e1}.main .nameContainer .inherited{display:inline-block;border-radius:3px;background-color:#888!important;font-size:.7em;padding:2px 4px;margin-right:5px}.main .nameContainer .inherited a{color:#fff}.main .nameContainer .tag-source{position:absolute;top:17px;right:0;font-size:10px}.main .nameContainer .tag-source a{color:gray}.main .nameContainer.inherited{color:gray}.main .nameContainer h4{margin-right:150px;line-height:1.3}.main .nameContainer h4 .signature{font-size:13px;font-weight:400;font-family:Menlo,Monaco,Consolas,"Courier New",monospace}.main .nameContainer h4 .type-signature.type a{color:#fff}.main pre{font-size:11px}.main table{width:100%;margin-bottom:15px}.main table th{padding:3px 3px}.main table td{vertical-align:top;padding:5px 3px}.main table .name{width:110px}.main table .type{width:60px;color:#aaa;font-size:11px}.main table .attributes{width:80px;color:#aaa;font-size:11px}.main table .description{font-size:12px}.main table .description p{margin:0}.main table .optional{float:left;border-radius:3px;background-color:#ddd!important;font-size:.7em;padding:2px 4px;margin-right:5px;color:gray}.main .readme p{margin-top:15px;line-height:1.2;font-size:.85em}.main .readme h1{font-size:1.7em}.main .readme h2{margin-top:30px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #e1e1e1}.main .readme li{font-size:.9em;margin-bottom:10px}.main article ol,.main article ul{margin-left:25px}.main article ol>li{list-style-type:decimal;margin-bottom:5px}.main article ul>li{margin-bottom:5px;list-style-type:disc}footer{margin:15px 0;padding-top:15px;border-top:1px solid #e1e1e1;font-family:freight-text-pro,Georgia,Cambria,"Times New Roman",Times,serif;font-size:.8em;color:gray}
--------------------------------------------------------------------------------
/docs/styles/prettify-jsdoc.css:
--------------------------------------------------------------------------------
1 | /* JSDoc prettify.js theme */
2 |
3 | /* plain text */
4 | .pln {
5 | color: #000000;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | /* string content */
11 | .str {
12 | color: #006400;
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
17 | /* a keyword */
18 | .kwd {
19 | color: #000000;
20 | font-weight: bold;
21 | font-style: normal;
22 | }
23 |
24 | /* a comment */
25 | .com {
26 | font-weight: normal;
27 | font-style: italic;
28 | }
29 |
30 | /* a type name */
31 | .typ {
32 | color: #000000;
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | /* a literal value */
38 | .lit {
39 | color: #006400;
40 | font-weight: normal;
41 | font-style: normal;
42 | }
43 |
44 | /* punctuation */
45 | .pun {
46 | color: #000000;
47 | font-weight: bold;
48 | font-style: normal;
49 | }
50 |
51 | /* lisp open bracket */
52 | .opn {
53 | color: #000000;
54 | font-weight: bold;
55 | font-style: normal;
56 | }
57 |
58 | /* lisp close bracket */
59 | .clo {
60 | color: #000000;
61 | font-weight: bold;
62 | font-style: normal;
63 | }
64 |
65 | /* a markup tag name */
66 | .tag {
67 | color: #006400;
68 | font-weight: normal;
69 | font-style: normal;
70 | }
71 |
72 | /* a markup attribute name */
73 | .atn {
74 | color: #006400;
75 | font-weight: normal;
76 | font-style: normal;
77 | }
78 |
79 | /* a markup attribute value */
80 | .atv {
81 | color: #006400;
82 | font-weight: normal;
83 | font-style: normal;
84 | }
85 |
86 | /* a declaration */
87 | .dec {
88 | color: #000000;
89 | font-weight: bold;
90 | font-style: normal;
91 | }
92 |
93 | /* a variable name */
94 | .var {
95 | color: #000000;
96 | font-weight: normal;
97 | font-style: normal;
98 | }
99 |
100 | /* a function name */
101 | .fun {
102 | color: #000000;
103 | font-weight: bold;
104 | font-style: normal;
105 | }
106 |
107 | /* Specify class=linenums on a pre to get line numbering */
108 | ol.linenums {
109 | margin-top: 0;
110 | margin-bottom: 0;
111 | }
112 |
--------------------------------------------------------------------------------
/docs/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Menlo, Monaco, Consolas, monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/styles/sunlight.dark.css:
--------------------------------------------------------------------------------
1 | /* global styles */
2 | .sunlight-container {
3 | clear: both !important;
4 | position: relative !important;
5 | margin: 10px 0 !important;
6 | }
7 | .sunlight-code-container {
8 | clear: both !important;
9 | position: relative !important;
10 | border: none;
11 | border-color: #626262 !important;
12 | background-color: #262626 !important;
13 | }
14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea {
15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important;
16 | font-size: 12px !important;
17 | line-height: 15px !important;
18 | }
19 | .sunlight-highlighted, .sunlight-container textarea {
20 | color: #FFFFFF !important;
21 | margin: 0 !important;
22 | }
23 | .sunlight-container textarea {
24 | padding-left: 0 !important;
25 | margin-left: 0 !important;
26 | margin-right: 0 !important;
27 | padding-right: 0 !important;
28 | }
29 | .sunlight-code-container > .sunlight-highlighted {
30 | white-space: pre;
31 | overflow-x: auto;
32 | overflow-y: hidden; /* ie requires this wtf? */
33 | }
34 | .sunlight-highlighted {
35 | z-index: 1;
36 | position: relative;
37 | }
38 | .sunlight-highlighted * {
39 | background: transparent;
40 | }
41 | .sunlight-line-number-margin {
42 | float: left !important;
43 | margin-right: 5px !important;
44 | margin-top: 0 !important;
45 | margin-bottom: 0 !important;
46 | padding: 0 !important;
47 | padding-right: 4px !important;
48 | padding-left: 4px !important;
49 | border-right: 1px solid #9A9A9A !important;
50 | background-color: #3E3E3E !important;
51 | color: #9A9A9A !important;
52 | text-align: right !important;
53 | position: relative;
54 | z-index: 3;
55 | }
56 | .sunlight-highlighted a, .sunlight-line-number-margin a {
57 | border: none !important;
58 | text-decoration: none !important;
59 | font-style: normal !important;
60 | padding: 0 !important;
61 | }
62 | .sunlight-line-number-margin a {
63 | color: inherit !important;
64 | }
65 | .sunlight-line-highlight-overlay {
66 | position: absolute;
67 | top: 0;
68 | left: 0;
69 | width: 100%;
70 | z-index: 0;
71 | }
72 | .sunlight-line-highlight-overlay div {
73 | height: 15px;
74 | width: 100%;
75 | }
76 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active {
77 | background-color: #4B4B4B;
78 | }
79 |
80 | /* menu */
81 | .sunlight-menu {
82 | background-color: #FFFFCC;
83 | color: #000000;
84 | }
85 | .sunlight-menu ul {
86 | margin: 0 !important;
87 | padding: 0 !important;
88 | list-style-type: none !important;
89 | }
90 | .sunlight-menu li {
91 | float: right !important;
92 | margin-left: 5px !important;
93 | }
94 | .sunlight-menu a, .sunlight-menu img {
95 | color: #000099 !important;
96 | text-decoration: none !important;
97 | border: none !important;
98 | }
99 |
100 |
101 |
102 |
103 | .sunlight-string,
104 | .sunlight-char,
105 | .sunlight-heredoc,
106 | .sunlight-heredocDeclaration,
107 | .sunlight-nowdoc,
108 | .sunlight-longString,
109 | .sunlight-rawString,
110 | .sunlight-binaryString,
111 | .sunlight-verbatimString,
112 | .sunlight-rawLongString,
113 | .sunlight-binaryLongString,
114 | .sunlight-diff .sunlight-added {
115 | color: #55EB54 !important;
116 | }
117 | .sunlight-operator,
118 | .sunlight-punctuation,
119 | .sunlight-delimiter {
120 | color: #B1EDEC !important;
121 | }
122 | .sunlight-ident,
123 | .sunlight-diff .sunlight-unchanged {
124 | color: #E0E0E0 !important;
125 | font-weight: bold !important;
126 | }
127 | .sunlight-comment,
128 | .sunlight-xmlDocCommentContent,
129 | .sunlight-nginx .sunlight-ssiCommand,
130 | .sunlight-sln .sunlight-formatDeclaration,
131 | .sunlight-diff .sunlight-mergeHeader,
132 | .sunlight-diff .sunlight-noNewLine {
133 | color: #787D31 !important;
134 | }
135 | .sunlight-number,
136 | .sunlight-cdata,
137 | .sunlight-guid,
138 | .sunlight-diff .sunlight-modified {
139 | color: #F7BA7E !important;
140 | font-weight: bold !important;
141 | }
142 | .sunlight-named-ident,
143 | .sunlight-xml .sunlight-attribute,
144 | .sunlight-constant,
145 | .sunlight-javascript .sunlight-globalVariable,
146 | .sunlight-globalObject,
147 | .sunlight-css .sunlight-id,
148 | .sunlight-python .sunlight-attribute,
149 | .sunlight-nginx .sunlight-context,
150 | .sunlight-httpd .sunlight-context,
151 | .sunlight-lisp .sunlight-declarationSpecifier,
152 | .sunlight-erlang .sunlight-userDefinedFunction,
153 | .sunlight-diff .sunlight-removed {
154 | color: #FBBDEE !important;
155 | font-weight: bold !important;
156 | }
157 | .sunlight-keyword,
158 | .sunlight-languageConstruct,
159 | .sunlight-specialOperator,
160 | .sunlight-xml .sunlight-tagName,
161 | .sunlight-xml .sunlight-operator,
162 | .sunlight-bash .sunlight-command,
163 | .sunlight-erlang .sunlight-moduleAttribute {
164 | color: #A3CCF7 !important;
165 | font-weight: bold !important;
166 | }
167 | .sunlight-shortOpenTag,
168 | .sunlight-openTag,
169 | .sunlight-closeTag,
170 | .sunlight-xmlOpenTag,
171 | .sunlight-xmlCloseTag,
172 | .sunlight-aspOpenTag,
173 | .sunlight-aspCloseTag,
174 | .sunlight-label,
175 | .sunlight-css .sunlight-importantFlag {
176 | background-color: #7373C1 !important;
177 | }
178 | .sunlight-content {
179 | color: #FFFFFF !important;
180 | font-weight: bold !important;
181 | }
182 | .sunlight-function,
183 | .sunlight-globalFunction,
184 | .sunlight-objective-c .sunlight-messageDestination,
185 | .sunlight-ruby .sunlight-specialFunction,
186 | .sunlight-6502asm .sunlight-illegalOpcode,
187 | .sunlight-powershell .sunlight-switch,
188 | .sunlight-lisp .sunlight-macro,
189 | .sunlight-lisp .sunlight-specialForm,
190 | .sunlight-lisp .sunlight-type,
191 | .sunlight-sln .sunlight-sectionName,
192 | .sunlight-diff .sunlight-header {
193 | color: #C8BBF1 !important;
194 | font-weight: bold !important;
195 | }
196 | .sunlight-variable,
197 | .sunlight-environmentVariable,
198 | .sunlight-specialVariable,
199 | .sunlight-objective-c .sunlight-messageArgumentName,
200 | .sunlight-lisp .sunlight-globalVariable,
201 | .sunlight-ruby .sunlight-globalVariable,
202 | .sunlight-ruby .sunlight-instanceVariable {
203 | color: #F5E5B0 !important;
204 | font-weight: bold !important;
205 | }
206 | .sunlight-regexLiteral,
207 | .sunlight-lisp .sunlight-operator,
208 | .sunlight-6502asm .sunlight-pseudoOp,
209 | .sunlight-erlang .sunlight-macro,
210 | .sunlight-diff .sunlight-rangeInfo {
211 | color: #E0F16A !important;
212 | }
213 | .sunlight-specialVariable {
214 | font-style: italic !important;
215 | font-weight: bold !important;
216 | }
217 | .sunlight-csharp .sunlight-pragma,
218 | .sunlight-preprocessorDirective,
219 | .sunlight-vb .sunlight-compilerDirective {
220 | color: #666363 !important;
221 | font-style: italic !important;
222 | }
223 | .sunlight-xmlDocCommentMeta,
224 | .sunlight-java .sunlight-annotation,
225 | .sunlight-scala .sunlight-annotation,
226 | .sunlight-docComment {
227 | color: #666363 !important;
228 | }
229 | .sunlight-quotedIdent,
230 | .sunlight-ruby .sunlight-subshellCommand,
231 | .sunlight-lisp .sunlight-keywordArgument,
232 | .sunlight-haskell .sunlight-infixOperator,
233 | .sunlight-erlang .sunlight-quotedAtom {
234 | color: #F8CA16 !important;
235 | }
236 |
237 |
238 |
239 |
240 | /* html/xml */
241 | .sunlight-xml .sunlight-tagName,
242 | .sunlight-xml .sunlight-operator,
243 | .sunlight-xml .sunlight-attribute {
244 | font-weight: normal !important;
245 | }
246 | .sunlight-doctype {
247 | color: #DEB9B2 !important;
248 | font-style: italic !important;
249 | }
250 | .sunlight-xml .sunlight-entity {
251 | background-color: #E6E585 !important;
252 | color: #000000 !important;
253 | }
254 |
255 | /* javascript */
256 | .sunlight-javascript .sunlight-reservedWord {
257 | font-style: italic !important;
258 | }
259 |
260 | /* css */
261 | .sunlight-css .sunlight-element {
262 | color: #E9EE97 !important;
263 | }
264 | .sunlight-css .sunlight-microsoftFilterPrefix {
265 | color: #C9FF9F !important;
266 | }
267 | .sunlight-css .sunlight-rule {
268 | color: #0099FF !important;
269 | }
270 | .sunlight-css .sunlight-class {
271 | color: #E78282 !important;
272 | }
273 | .sunlight-css .sunlight-pseudoClass, .sunlight-css .sunlight-pseudoElement {
274 | color: #73D693 !important;
275 | }
276 |
277 | /* bash */
278 | .sunlight-bash .sunlight-hashBang {
279 | color: #FFFF00 !important;
280 | }
281 |
282 | .sunlight-bash .sunlight-verbatimCommand {
283 | color: #BBA4EE !important;
284 | }
285 | .sunlight-bash .sunlight-variable,
286 | .sunlight-bash .sunlight-specialVariable {
287 | color: #ED8585 !important;
288 | }
289 |
290 | /* python */
291 | .sunlight-python .sunlight-specialMethod {
292 | font-weight: bold !important;
293 | color: #B0A3C2;
294 | }
295 |
296 | /* ruby */
297 | .sunlight-ruby .sunlight-symbol {
298 | font-weight: bold !important;
299 | color: #90EEA2 !important;
300 | }
301 |
302 | /* brainfuck */
303 | .sunlight-brainfuck {
304 | font-weight: bold !important;
305 | color: #000000 !important;
306 | }
307 | .sunlight-brainfuck .sunlight-increment {
308 | background-color: #FF9900 !important;
309 | }
310 | .sunlight-brainfuck .sunlight-decrement {
311 | background-color: #FF99FF !important;
312 | }
313 | .sunlight-brainfuck .sunlight-incrementPointer {
314 | background-color: #FFFF99 !important;
315 | }
316 | .sunlight-brainfuck .sunlight-decrementPointer {
317 | background-color: #66CCFF !important;
318 | }
319 | .sunlight-brainfuck .sunlight-read {
320 | background-color: #FFFFFF !important;
321 | }
322 | .sunlight-brainfuck .sunlight-write {
323 | background-color: #99FF99 !important;
324 | }
325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop {
326 | background-color: #FFFFFF !important;
327 | }
328 |
329 | /* 6502 asm */
330 | .sunlight-6502asm .sunlight-label {
331 | background: none !important;
332 | color: #FFFFFF !important;
333 | text-decoration: underline !important;
334 | }
335 |
336 | /* lisp */
337 | .sunlight-lisp .sunlight-macro {
338 | font-style: italic !important;
339 | }
340 |
341 | /* erlang */
342 | .sunlight-erlang .sunlight-atom {
343 | color: #FFFFFF !important;
344 | font-weight: bold !important;
345 | }
--------------------------------------------------------------------------------
/docs/styles/sunlight.default.css:
--------------------------------------------------------------------------------
1 | /* global styles */
2 | .sunlight-container {
3 | clear: both !important;
4 | position: relative !important;
5 | margin: 10px 0 !important;
6 | }
7 | .sunlight-code-container {
8 | clear: both !important;
9 | position: relative !important;
10 | border: none;
11 | border-color: #969696 !important;
12 | background-color: #FFFFFF !important;
13 | }
14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea {
15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important;
16 | font-size: 12px !important;
17 | line-height: 15px !important;
18 | }
19 | .sunlight-highlighted, .sunlight-container textarea {
20 | color: #000000 !important;
21 | margin: 0 !important;
22 | }
23 | .sunlight-container textarea {
24 | padding-left: 0 !important;
25 | margin-left: 0 !important;
26 | margin-right: 0 !important;
27 | padding-right: 0 !important;
28 | }
29 | .sunlight-code-container > .sunlight-highlighted {
30 | white-space: pre;
31 | overflow-x: auto;
32 | overflow-y: hidden; /* ie requires this wtf? */
33 | }
34 | .sunlight-highlighted {
35 | z-index: 1;
36 | position: relative;
37 | }
38 | .sunlight-highlighted * {
39 | background: transparent;
40 | }
41 | .sunlight-line-number-margin {
42 | float: left !important;
43 | margin-right: 5px !important;
44 | margin-top: 0 !important;
45 | margin-bottom: 0 !important;
46 | padding: 0 !important;
47 | padding-right: 4px !important;
48 | padding-left: 4px !important;
49 | border-right: 1px solid #CCCCCC !important;
50 | background-color: #EEEEEE !important;
51 | color: #848484 !important;
52 | text-align: right !important;
53 | position: relative;
54 | z-index: 3;
55 | }
56 | .sunlight-highlighted a, .sunlight-line-number-margin a {
57 | border: none !important;
58 | text-decoration: none !important;
59 | font-weight: normal !important;
60 | font-style: normal !important;
61 | padding: 0 !important;
62 | }
63 | .sunlight-line-number-margin a {
64 | color: inherit !important;
65 | }
66 | .sunlight-line-highlight-overlay {
67 | position: absolute;
68 | top: 0;
69 | left: 0;
70 | width: 100%;
71 | z-index: 0;
72 | }
73 | .sunlight-line-highlight-overlay div {
74 | height: 15px;
75 | width: 100%;
76 | }
77 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active {
78 | background-color: #E7FCFA;
79 | }
80 |
81 | /* menu */
82 | .sunlight-menu {
83 | background-color: #FFFFCC;
84 | color: #000000;
85 | }
86 | .sunlight-menu ul {
87 | margin: 0 !important;
88 | padding: 0 !important;
89 | list-style-type: none !important;
90 | }
91 | .sunlight-menu li {
92 | float: right !important;
93 | margin-left: 5px !important;
94 | }
95 | .sunlight-menu a, .sunlight-menu img {
96 | color: #000099 !important;
97 | text-decoration: none !important;
98 | border: none !important;
99 | }
100 |
101 |
102 |
103 |
104 | .sunlight-string,
105 | .sunlight-char,
106 | .sunlight-heredoc,
107 | .sunlight-heredocDeclaration,
108 | .sunlight-nowdoc,
109 | .sunlight-longString,
110 | .sunlight-rawString,
111 | .sunlight-binaryString,
112 | .sunlight-rawLongString,
113 | .sunlight-binaryLongString,
114 | .sunlight-verbatimString,
115 | .sunlight-diff .sunlight-removed {
116 | color: #990000 !important;
117 | }
118 |
119 | .sunlight-ident,
120 | .sunlight-operator,
121 | .sunlight-punctuation,
122 | .sunlight-delimiter,
123 | .sunlight-diff .sunlight-unchanged {
124 | color: #000000 !important;
125 | }
126 |
127 | .sunlight-comment,
128 | .sunlight-xmlDocCommentContent,
129 | .sunlight-nginx .sunlight-ssiCommand,
130 | .sunlight-sln .sunlight-formatDeclaration,
131 | .sunlight-diff .sunlight-added {
132 | color: #009900 !important;
133 | }
134 | .sunlight-number,
135 | .sunlight-guid,
136 | .sunlight-cdata {
137 | color: #CC6600 !important;
138 | }
139 |
140 | .sunlight-named-ident,
141 | .sunlight-constant,
142 | .sunlight-javascript .sunlight-globalVariable,
143 | .sunlight-globalObject,
144 | .sunlight-python .sunlight-attribute,
145 | .sunlight-nginx .sunlight-context,
146 | .sunlight-httpd .sunlight-context,
147 | .sunlight-haskell .sunlight-class,
148 | .sunlight-haskell .sunlight-type,
149 | .sunlight-lisp .sunlight-declarationSpecifier,
150 | .sunlight-erlang .sunlight-userDefinedFunction,
151 | .sunlight-diff .sunlight-header {
152 | color: #2B91AF !important;
153 | }
154 | .sunlight-keyword,
155 | .sunlight-languageConstruct,
156 | .sunlight-css
157 | .sunlight-element,
158 | .sunlight-bash .sunlight-command,
159 | .sunlight-specialOperator,
160 | .sunlight-erlang .sunlight-moduleAttribute,
161 | .sunlight-xml .sunlight-tagName,
162 | .sunlight-xml .sunlight-operator,
163 | .sunlight-diff .sunlight-modified {
164 | color: #0000FF !important;
165 | }
166 | .sunlight-shortOpenTag,
167 | .sunlight-openTag,
168 | .sunlight-closeTag,
169 | .sunlight-xmlOpenTag,
170 | .sunlight-xmlCloseTag,
171 | .sunlight-aspOpenTag,
172 | .sunlight-aspCloseTag,
173 | .sunlight-label,
174 | .sunlight-css .sunlight-importantFlag {
175 | background-color: #FFFF99 !important;
176 | color: #000000 !important;
177 | }
178 | .sunlight-function,
179 | .sunlight-globalFunction,
180 | .sunlight-ruby .sunlight-specialFunction,
181 | .sunlight-objective-c .sunlight-messageDestination,
182 | .sunlight-6502asm .sunlight-illegalOpcode,
183 | .sunlight-powershell .sunlight-switch,
184 | .sunlight-lisp .sunlight-macro,
185 | .sunlight-lisp .sunlight-specialForm,
186 | .sunlight-lisp .sunlight-type,
187 | .sunlight-sln .sunlight-sectionName,
188 | .sunlight-diff .sunlight-rangeInfo {
189 | color: #B069AF !important;
190 | }
191 |
192 | .sunlight-variable,
193 | .sunlight-specialVariable,
194 | .sunlight-environmentVariable,
195 | .sunlight-objective-c .sunlight-messageArgumentName,
196 | .sunlight-lisp .sunlight-globalVariable,
197 | .sunlight-ruby .sunlight-globalVariable,
198 | .sunlight-ruby .sunlight-instanceVariable,
199 | .sunlight-sln .sunlight-operator {
200 | color: #325484 !important;
201 | }
202 | .sunlight-regexLiteral,
203 | .sunlight-lisp .sunlight-operator,
204 | .sunlight-6502asm .sunlight-pseudoOp,
205 | .sunlight-erlang .sunlight-macro {
206 | color: #FF00B2 !important;
207 | }
208 | .sunlight-specialVariable {
209 | font-style: italic !important;
210 | font-weight: bold !important;
211 | }
212 | .sunlight-csharp .sunlight-pragma,
213 | .sunlight-preprocessorDirective,
214 | .sunlight-vb .sunlight-compilerDirective,
215 | .sunlight-diff .sunlight-mergeHeader,
216 | .sunlight-diff .sunlight-noNewLine {
217 | color: #999999 !important;
218 | font-style: italic !important;
219 | }
220 | .sunlight-xmlDocCommentMeta,
221 | .sunlight-java .sunlight-annotation,
222 | .sunlight-scala .sunlight-annotation,
223 | .sunlight-docComment {
224 | color: #808080 !important;
225 | }
226 | .sunlight-quotedIdent,
227 | .sunlight-ruby .sunlight-subshellCommand,
228 | .sunlight-lisp .sunlight-keywordArgument,
229 | .sunlight-haskell .sunlight-infixOperator,
230 | .sunlight-erlang .sunlight-quotedAtom {
231 | color: #999900 !important;
232 | }
233 |
234 |
235 |
236 | /* xml */
237 | .sunlight-xml .sunlight-string {
238 | color: #990099 !important;
239 | }
240 | .sunlight-xml .sunlight-attribute {
241 | color: #FF0000 !important;
242 | }
243 | .sunlight-xml .sunlight-entity {
244 | background-color: #EEEEEE !important;
245 | color: #000000 !important;
246 | border: 1px solid #000000 !important;
247 | }
248 | .sunlight-xml .sunlight-doctype {
249 | color: #2B91AF !important;
250 | }
251 |
252 | /* javascript */
253 | .sunlight-javascript .sunlight-reservedWord {
254 | font-style: italic !important;
255 | }
256 |
257 | /* css */
258 | .sunlight-css .sunlight-microsoftFilterPrefix {
259 | color: #FF00FF !important;
260 | }
261 | .sunlight-css .sunlight-rule {
262 | color: #0099FF !important;
263 | }
264 | .sunlight-css .sunlight-keyword {
265 | color: #4E65B8 !important;
266 | }
267 | .sunlight-css .sunlight-class {
268 | color: #FF0000 !important;
269 | }
270 | .sunlight-css .sunlight-id {
271 | color: #8A8E13 !important;
272 | }
273 | .sunlight-css .sunlight-pseudoClass,
274 | .sunlight-css .sunlight-pseudoElement {
275 | color: #368B87 !important;
276 | }
277 |
278 | /* bash */
279 | .sunlight-bash .sunlight-hashBang {
280 | color: #3D97F5 !important;
281 | }
282 | .sunlight-bash .sunlight-verbatimCommand {
283 | color: #999900 !important;
284 | }
285 | .sunlight-bash .sunlight-variable,
286 | .sunlight-bash .sunlight-specialVariable {
287 | color: #FF0000 !important;
288 | }
289 |
290 | /* python */
291 | .sunlight-python .sunlight-specialMethod {
292 | font-weight: bold !important;
293 | color: #A07DD3;
294 | }
295 |
296 | /* ruby */
297 | .sunlight-ruby .sunlight-symbol {
298 | font-weight: bold !important;
299 | color: #ED7272 !important;
300 | }
301 |
302 | /* brainfuck */
303 | .sunlight-brainfuck {
304 | font-weight: bold !important;
305 | color: #000000 !important;
306 | }
307 | .sunlight-brainfuck .sunlight-increment {
308 | background-color: #FF9900 !important;
309 | }
310 | .sunlight-brainfuck .sunlight-decrement {
311 | background-color: #FF99FF !important;
312 | }
313 | .sunlight-brainfuck .sunlight-incrementPointer {
314 | background-color: #FFFF99 !important;
315 | }
316 | .sunlight-brainfuck .sunlight-decrementPointer {
317 | background-color: #66CCFF !important;
318 | }
319 | .sunlight-brainfuck .sunlight-read {
320 | background-color: #FFFFFF !important;
321 | }
322 | .sunlight-brainfuck .sunlight-write {
323 | background-color: #99FF99 !important;
324 | }
325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop {
326 | background-color: #FFFFFF !important;
327 | }
328 |
329 | /* 6502 asm */
330 | .sunlight-6502asm .sunlight-label {
331 | font-weight: bold !important;
332 | color: #000000 !important;
333 | background: none !important;
334 | }
335 |
336 | /* lisp */
337 | .sunlight-lisp .sunlight-macro {
338 | font-style: italic !important;
339 | }
340 |
341 | /* erlang */
342 | .sunlight-erlang .sunlight-atom {
343 | font-weight: bold !important;
344 | }
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Include required modules
4 | const gulp = require('gulp');
5 | const babelify = require('babelify');
6 | const babel = require('gulp-babel');
7 | const browserify = require('browserify');
8 | const connect = require('gulp-connect');
9 | const source = require('vinyl-source-stream');
10 | const buffer = require('vinyl-buffer');
11 | const sourcemaps = require('gulp-sourcemaps');
12 | const eslint = require('gulp-eslint');
13 | const jsdoc = require('gulp-jsdoc3');
14 | const uglify = require('gulp-uglify');
15 | const rename = require('gulp-rename');
16 | const del = require('del');
17 | const mochaPhantomJS = require('gulp-mocha-phantomjs');
18 |
19 | // paths
20 | const NAME = require('./package.json').name;
21 | const DOC_PATH = './src/**/*.js';
22 | const SRC_FILES = DOC_PATH;
23 | const LIB_PATH = './lib';
24 | const LINT_PATH = DOC_PATH;
25 | const BUILD_OUTPUT = './dist';
26 | const DEMOS_OUTPUT = BUILD_OUTPUT + '/demos';
27 | const THEME_PATH = './styleSheets';
28 |
29 | // Default task. This will be run when no task is passed in arguments to gulp
30 | gulp.task('default', ['uglify', 'doc']);
31 |
32 | // test
33 | gulp.task('test', ['buildTest'], function() {
34 | return gulp
35 | .src('test/runner.html')
36 | .pipe(mochaPhantomJS({
37 | reporter: 'spec',
38 | phantomjs: {
39 | useColors: true,
40 | },
41 | }));
42 | });
43 |
44 | // clean
45 | gulp.task('clean', function(cb) {
46 | return del(['dist', 'doc', 'lib'], cb);
47 | });
48 |
49 | // lint with eslint
50 | gulp.task('lint', () => {
51 | return gulp.src([LINT_PATH, '!node_modules/**'])
52 | .pipe(eslint())
53 | .pipe(eslint.format())
54 | .pipe(eslint.failAfterError());
55 | });
56 |
57 | // Copy static files from html folder to dist folder
58 | gulp.task('copyDemos', ['clean', 'lint'], function() {
59 | return gulp.src('./src/demos/**/*')
60 | .pipe(gulp.dest(DEMOS_OUTPUT));
61 | });
62 |
63 | // Copy themes to dist
64 | gulp.task('copyStyles', ['copyDemos'], function() {
65 | return gulp.src(THEME_PATH + '/**/*')
66 | .pipe(gulp.dest(BUILD_OUTPUT + '/styleSheets'));
67 | });
68 |
69 | // jsdoc
70 | gulp.task('doc', function(cb) {
71 | let config = require('./jsdoc.json');
72 | gulp.src(['README.md', DOC_PATH], {read: false})
73 | .pipe(jsdoc(config, cb));
74 | });
75 |
76 | gulp.task('buildTest', ()=>{
77 | return browserify({
78 | entries: ['./test/index.js'],
79 | debug: true, /* !gulp.env.production,*/
80 | insertGlobals: true,
81 | })
82 | .transform(babelify.configure({presets: ['es2015']}))
83 | .bundle()
84 | .pipe(source('test.js'))
85 | .pipe(gulp.dest('./test'))
86 | ;
87 | });
88 |
89 | // Convert ES6 code in all js files in src/js folder and copy to
90 | // dist folder
91 | gulp.task('build', ['copyStyles'], function() {
92 | return browserify({
93 | entries: ['./src/index.js'],
94 | debug: false, /* !gulp.env.production,*/
95 | // insertGlobals: true,
96 | })
97 | // .pipe(sourcemaps.init())
98 | .transform(babelify.configure({presets: ['es2015']}))
99 | .bundle()
100 | .pipe(source(NAME + '.js'))
101 | // .pipe(sourcemaps.write(BUILD_OUTPUT))
102 | .pipe(gulp.dest(BUILD_OUTPUT))
103 | ;
104 | });
105 |
106 | // Convert ES6 code in all js files in src/js folder and copy to
107 | // dist folder
108 | gulp.task('npmbuild', ['copyStyles'], function() {
109 | return gulp.src(SRC_FILES)
110 | .pipe(sourcemaps.init())
111 | .pipe(babel({
112 | presets: ['es2015'],
113 | }))
114 | .pipe(sourcemaps.write('.'))
115 | .pipe(gulp.dest(LIB_PATH));
116 | });
117 |
118 | // uglify
119 | gulp.task('uglify', ['build'], ()=> {
120 | return gulp.src([BUILD_OUTPUT + '/' + NAME + '.js'])
121 | .pipe(sourcemaps.init())
122 | .pipe(buffer())
123 | .pipe(uglify({
124 | compress: {
125 | passes: 10,
126 | sequences: true,
127 | dead_code: true,
128 | conditionals: true,
129 | booleans: true,
130 | unused: true,
131 | if_return: true,
132 | join_vars: true,
133 | drop_console: true,
134 | unsafe: true,
135 | unsafe_proto: true,
136 | evaluate: true,
137 | loops: true,
138 | if_return: true,
139 | join_vars: true,
140 | cascade: true,
141 | collapse_vars: true,
142 | reduce_vars: true,
143 | },
144 | output: {
145 | beautify: false,
146 | semicolons: false,
147 | quote_style: 1,
148 | },
149 | mangle: true}))
150 | .pipe(rename(NAME + '.min.js'))
151 | .pipe(sourcemaps.write('.'))
152 | .pipe(gulp.dest(BUILD_OUTPUT));
153 | });
154 |
155 | gulp.task('cleanWWW', function(cb) {
156 | return del(['www'], cb);
157 | });
158 |
159 | gulp.task('precocoon', ['cleanWWW'], function() {
160 | gulp.src('./src/demos/**/*')
161 | .pipe(gulp.dest('./www/'));
162 | gulp.src('./styleSheets/**/*')
163 | .pipe(gulp.dest('./www/'));
164 | gulp.src('./dist/sabertooth.min.js')
165 | .pipe(gulp.dest('./www/'));
166 | });
167 |
168 |
169 | // Start a test server with doc root at dist folder and
170 | // listening to 9001 port. Home page = http://localhost:9001
171 | gulp.task('startServer', function() {
172 | connect.server({
173 | root: DEMOS_OUTPUT,
174 | livereload: true,
175 | port: 9001,
176 | });
177 | });
178 |
--------------------------------------------------------------------------------
/inch.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "included": [
4 | "src/**/*.js"
5 | ],
6 | "excluded": [
7 |
8 | ]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": true
4 | },
5 |
6 | "source":{
7 | "includePattern": ".+\\.js(doc)?$",
8 | "excludePattern": "(^|\\/|\\\\)_"
9 | },
10 |
11 | "opts": {
12 | "destination": "./docs",
13 | "recurse": true,
14 | "verbose": true,
15 | "private": true,
16 | "encoding": "utf8",
17 | "lenient": true,
18 | "template": "./node_modules/jaguarjs-jsdoc"
19 | },
20 |
21 | "plugins": [
22 | "plugins/markdown",
23 | "./scripts/fixJsdoc.js"
24 | ],
25 |
26 | "templates": {
27 | "cleverLinks": false,
28 | "monospaceLinks": false,
29 | "default": {
30 | "outputSourceFiles": true
31 | },
32 | "linenums": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sabertooth",
3 | "version": "0.2.1",
4 | "description": "A WebGL UI framework built on pixi.js and inspired by QT",
5 | "main": "./lib/index.js",
6 | "scripts": {
7 | "test": "gulp test"
8 | },
9 | "keywords": [
10 | "WebGL",
11 | "UI",
12 | "user interface",
13 | "pixi",
14 | "2d",
15 | "mobile",
16 | "desktop",
17 | "web",
18 | "apps"
19 | ],
20 | "author": "Zach Moore
",
21 | "license": "ISC",
22 | "bugs": {
23 | "url": "github.com/AbydosDigital/SaberTooth/issues",
24 | "email": "AbydosDigital@gmail.com"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/AbydosDigital/SaberTooth.git"
29 | },
30 | "devDependencies": {
31 | "babel-plugin-version-inline": "^1.0.0",
32 | "babel-preset-es2015": "^6.22.0",
33 | "babelify": "^7.3.0",
34 | "browserify": "^14.1.0",
35 | "chai": "^3.5.0",
36 | "eslint": "^3.14.0",
37 | "eslint-config-google": "^0.7.1",
38 | "eslint-plugin-import": "^2.2.0",
39 | "gulp": "^3.9.1",
40 | "gulp-babel": "^6.1.2",
41 | "gulp-connect": "^5.7.0",
42 | "gulp-eslint": "^3.0.1",
43 | "gulp-inject": "^4.2.0",
44 | "gulp-jsdoc3": "^1.0.1",
45 | "gulp-mocha": "^4.1.0",
46 | "gulp-mocha-phantomjs": "^0.12.1",
47 | "gulp-rename": "^1.2.2",
48 | "gulp-sourcemaps": "^2.4.1",
49 | "gulp-uglify": "^2.1.2",
50 | "jaguarjs-jsdoc": "^1.0.2",
51 | "jsdoc": "^3.4.3",
52 | "mocha": "^3.3.0",
53 | "mocha-phantomjs": "^4.1.0",
54 | "phantomjs": "^1.9.7-15",
55 | "pixi.js": "^4.5.1",
56 | "sinon": "^2.2.0",
57 | "vinyl-buffer": "^1.0.0",
58 | "vinyl-source-stream": "^1.1.0"
59 | },
60 | "dependencies": {
61 | "babel": "^6.23.0",
62 | "babel-polyfill": "^6.23.0",
63 | "babel-register": "^6.24.0",
64 | "del": "^2.2.2",
65 | "eventemitter3": "^2.0.2",
66 | "pixi-gl-core": "^1.1.0",
67 | "pixi.js": "^4.5.1"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zachacious/Sabertooth/02840b880fbb75f30b993398cd7627af08f911b7/screenshot.jpg
--------------------------------------------------------------------------------
/scripts/fixJsdoc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // JSDoc has issues getting the name of `export default class NAME {}`
4 | // this gross regex hacks around that issue until it is fixed.
5 | // See: https://github.com/jsdoc3/jsdoc/issues/1137#issuecomment-174829004
6 |
7 | const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g;
8 | const grossReplace = 'export default $2;\n\n$1\nclass $2';
9 |
10 | // JSDoc has issues with expressing member properties within a class
11 | // this is another terrible hack to address this issue.
12 | // See: https://github.com/jsdoc3/jsdoc/issues/1301
13 |
14 | const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g;
15 | const rgxClassName = /export (default )?class (.+?)\s/;
16 | const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/;
17 |
18 | exports.handlers = {
19 | /**
20 | * Called before parsing a file, giving us a change to replace the source.
21 | *
22 | * @param {*} e - The `beforeParse` event data.
23 | * @param {string} e.filename - The name of the file being parsed.
24 | * @param {string} e.source - The source of the file being parsed.
25 | */
26 | beforeParse(e) {
27 | const namespace = e.source.match(rgxNamespace);
28 | const className = e.source.match(rgxClassName);
29 |
30 | // Fix members not showing up attached to class
31 | if (namespace && className) {
32 | // console.log(`${namespace[1]}.${className[2]}`);
33 | // Replaces "@member {Type}"" with "@member {Type}
34 | // PIXI.ClassName#prop"
35 | e.source = e.source
36 | .replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`);
37 | }
38 |
39 | e.source = e.source.replace(rgxGross, grossReplace);
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import * as PIXI from 'pixi.js';
2 | // import {autoDetectRenderer} from 'pixi.js/lib/core';
3 | import Theme from './Theme';
4 | import StageWidget from './widgets/StageWidget';
5 | import BaseWidget from './widgets/BaseWidget';
6 | import EventEmitter from 'eventemitter3';
7 |
8 | /**
9 | * App will setup PIXI renderer and root widget automatically.
10 | * Just call App.update() from your render loop
11 | * @class
12 | * @memberof ST
13 | * @extends external:EventEmitter
14 | *
15 | * @example
16 | * let myApp = new ST.App({
17 | * name: 'My Demo App',
18 | * antialiasing: true,
19 | * });
20 | *
21 | * let w = new ST.Widgets.Button(myApp.root, {width: 100, height: 30});
22 | *
23 | * let main = function() {
24 | * myApp.update();
25 | * requestAnimationFrame(main);
26 | * };
27 | *
28 | * //call main which will initiate the loop
29 | * main();
30 | */
31 | export default class App extends EventEmitter {
32 | /**
33 | * @param {Object} options - Optional parameters
34 | * @param {String} [options.name] - The apps name
35 | * @param {ST.Theme} [options.theme]
36 | * - Theme used by the app - by default creates
37 | * a new Theme with default settings.
38 | * @param {Boolean} [options.transparentBkg=false] - True makes
39 | * background transparent
40 | * @param {Boolean} [options.antialiasing=false] - Sets antialiasing
41 | * @param {Boolean} [options.forceFXAA=false] - Lower quality antialiasing but
42 | * performs better
43 | * @param {Number} [options.resolution=1] - Pixi renderer resolution
44 | * @param {Number} [options.width=800] - Width of app renderer
45 | * @param {Number} [options.height=600] - Height of app renderer
46 | * @param {Boolean} [options.autoResize=true] - True makes app resize with
47 | * browser automatically
48 | */
49 | constructor(options = {}) {
50 | super();
51 | const defaults = {
52 | name: 'My App',
53 | theme: new Theme(),
54 | transparentBkg: false,
55 | antialiasing: false,
56 | forceFXAA: false,
57 | resolution: window.devicePixelRatio,
58 | width: 800,
59 | height: 600,
60 | autoResize: true,
61 | forceWebgl: false,
62 | };
63 |
64 | // fill in missing options with defaults
65 | options = Object.assign(defaults, options);
66 |
67 | /**
68 | * The internal name of the app
69 | * @type {String}
70 | * @private
71 | */
72 | this._name = options.name;
73 |
74 | // html docs title tag
75 | document.title = this._name;
76 |
77 | const renderOptions = {
78 | resolution: options.resolution,
79 | forceFXAA: options.forceFXAA,
80 | antialias: options.antialiasing,
81 | transparent: options.transparent,
82 | autoResize: true,
83 | roundPixels: true,
84 | };
85 |
86 | /**
87 | * Points to the correct PIXI renderer. Webgl if possible or if
88 | * forced.
89 | * @member {PIXI.WebglRenderer | PIXI.CanvasRenderer}
90 | */
91 | if(!options.forceWebgl) {
92 | this.renderer = PIXI.autoDetectRenderer(options.width, options.height,
93 | renderOptions);
94 | } else {
95 | renderOptions.width = options.width;
96 | renderOptions.height = options.height;
97 | this.renderer = new PIXI.WebGLRenderer(renderOptions);
98 | }
99 |
100 |
101 | // Add webgl canvas to the doc. Set padding and margins to 0
102 | this.renderer.view.screencanvas = true;
103 | document.body.appendChild(this.renderer.view);
104 | let newStyle = document.createElement('style');
105 | let style = '* {padding: 0; margin: 0}';
106 | newStyle.appendChild(document.createTextNode(style));
107 | document.head.appendChild(newStyle);
108 |
109 | /**
110 | * Internal theme for app
111 | * @member {ST.Theme}
112 | * @private
113 | */
114 | this._theme = options.theme;
115 |
116 | /**
117 | * All widgets should be children or children of children of the
118 | * root widget.
119 | *
120 | * @member {ST.Widgets.BaseWidget}
121 | */
122 | this.root = new StageWidget();
123 | this.root.padding.setAllTo(0);
124 |
125 | this.renderer.backgroundColor = this._theme.background;
126 |
127 | if(options.autoResize) {
128 | this.resizeToWindow();
129 | } else {
130 | this.renderer.resize(options.width, options.height);
131 | this.root.width = options.width;
132 | this.root.height = options.height;
133 | }
134 |
135 | /**
136 | * Internal autoResize
137 | * @private
138 | */
139 | this._autoResize = false;
140 | this.autoResize = options.autoResize;
141 |
142 | // listen for window resize. emit signal
143 | window.addEventListener('resize', (e)=>{
144 | this.emit('resize');
145 | });
146 | // listen for orientation changes. emit signal.(mobile only)
147 | window.addEventListener('orientationchange', (e)=>{
148 | this.emit('resize');
149 | });
150 |
151 | /**
152 | * Fires when the window is resized
153 | * @event ST.App#resize
154 | */
155 | }
156 |
157 | /**
158 | * Resizes the renderer and root widget to match the browser size
159 | */
160 | resizeToWindow() {
161 | this.renderer.resize(window.innerWidth, window.innerHeight);
162 | this.root.width = window.innerWidth;
163 | this.root.height = window.innerHeight;
164 | }
165 |
166 | /**
167 | * Renders the root widget
168 | */
169 | update() {
170 | let focus = BaseWidget.getFocusedWidget();
171 | if(focus) {
172 | focus.onHasFocus();
173 | }
174 |
175 | this.renderer.render(this.root);
176 | }
177 |
178 | /**
179 | * The name of the app
180 | * @member {String}
181 | */
182 | get name() {
183 | return this._name;
184 | }
185 |
186 | set name(val) { // eslint-disable-line require-jsdoc
187 | this._name = val;
188 | document.title = val; // html doc's title tag
189 | }
190 |
191 | /**
192 | * The theme for the app
193 | * @member {ST.Theme}
194 | */
195 | get theme() {
196 | return this._theme;
197 | }
198 |
199 | set theme(val) { // eslint-disable-line require-jsdoc
200 | this._theme = val;
201 | this.renderer.backgroundColor = this._theme.background;
202 | this.root.theme = this._theme;
203 | }
204 |
205 | /**
206 | *If true the app will auto-size itself to fit the browser window
207 | *@member {Boolean}
208 | */
209 | get autoResize() {
210 | return this._autoResize;
211 | }
212 |
213 | set autoResize(val) { // eslint-disable-line require-jsdoc
214 | this._autoResize = val;
215 | const listeners = this.listeners('resize');
216 |
217 | if(val) {
218 | if(listeners.indexOf(this.resizeToWindow) === -1) {
219 | // listener doesnt exist so add it
220 | this.on('resize', this.resizeToWindow, this);
221 | }
222 | } else {
223 | if(listeners.indexOf(this.resizeToWindow) !== -1) {
224 | // listener exist so remove it
225 | this.removeListener('resize', this.resizeToWindow);
226 | }
227 | }
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/src/GraphicsGen.js:
--------------------------------------------------------------------------------
1 | import * as PIXI from 'pixi.js';
2 | // import {Graphics} from 'pixi.js/lib/core';
3 | /**
4 | * Methods that generate Pixi.Graphics
5 | * @memberof ST
6 | */
7 | export default class GraphicsGen {
8 |
9 | /**
10 | * Generate a rectangle as a graphic
11 | * @param {Number} width - width of rectangle
12 | * @param {Number} height - height of rectangle
13 | * @param {Number} color - color of rectangle
14 | * @static
15 | * @return {PIXI.Graphics}
16 | */
17 | static rectangleGraphic(width, height, color) {
18 | let graphic = new PIXI.Graphics();
19 | graphic.beginFill(color);
20 | graphic.drawRect(0, 0, width, height);
21 | graphic.endFill();
22 | return graphic;
23 | }
24 |
25 | /**
26 | * Generate a rectangle as a texture
27 | * @param {Number} width - width of rectangle
28 | * @param {Number} height - height of rectangle
29 | * @param {Number} color - color of rectangle
30 | * @static
31 | * @return {PIXI.Texture}
32 | */
33 | static rectangleTexture(width, height, color) {
34 | let graphic = GraphicsGen.rectangleGraphic(width, height, color);
35 | return graphic.generateCanvasTexture();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Padding.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3';
2 |
3 | /**
4 | * Padding componenet holds padding data for 4 sides
5 | * @memberof ST
6 | * @extends external:EventEmitter
7 | */
8 | export default class Padding extends EventEmitter {
9 | /**
10 | * @param {Number} [top=0]
11 | * @param {Number} [left=0]
12 | * @param {Number} [bottom=0]
13 | * @param {Number} [right=0]
14 | */
15 | constructor(top = 0, left = 0, bottom = 0, right = 0) {
16 | super();
17 |
18 | /**
19 | * Internal top padding
20 | * @member {Number}
21 | * @private
22 | */
23 | this._top = top;
24 |
25 | /**
26 | * Internal left padding
27 | * @member {Number}
28 | * @private
29 | */
30 | this._left = left;
31 |
32 | /**
33 | * Internal bottom padding
34 | * @member {Number}
35 | * @private
36 | */
37 | this._bottom = bottom;
38 |
39 | /**
40 | * Internal right padding
41 | * @member {Number}
42 | * @private
43 | */
44 | this._right = right;
45 |
46 | /**
47 | * Fires when one or more of the paddings have changed
48 | * @event ST.Padding#changed
49 | */
50 | }
51 |
52 | /**
53 | * Convienent way to set all paddings to different values
54 | * @param {Number} [top=0]
55 | * @param {Number} [left=0]
56 | * @param {Number} [bottom=0]
57 | * @param {Number} [right=0]
58 | * @return {Array} - Contains padding values
59 | */
60 | set(top = 0, left = 0, bottom = 0, right = 0) {
61 | this._top = top;
62 | this._left = left;
63 | this._bottom = bottom;
64 | this._right = right;
65 | this.emit('changed', this);
66 | return [top, left, bottom, right];
67 | }
68 |
69 | /**
70 | * Set all padding to the same value
71 | * @param {Number} [val=0] - the amount of padding
72 | * @return {Number} - the padding
73 | */
74 | setAllTo(val = 0) {
75 | this._top = this._bottom = this._left = this._right = val;
76 | this.emit('changed', this);
77 | return val;
78 | }
79 |
80 | /**
81 | * Top padding
82 | * @member {Number}
83 | */
84 | get top() {
85 | return this._top;
86 | }
87 |
88 | set top(val) { // eslint-disable-line require-jsdoc
89 | this._top = val;
90 | this.emit('changed', this);
91 | }
92 |
93 | /**
94 | * Left padding
95 | * @member {Number}
96 | */
97 | get left() {
98 | return this._left;
99 | }
100 |
101 | set left(val) { // eslint-disable-line require-jsdoc
102 | this._left = val;
103 | this.emit('changed', this);
104 | }
105 |
106 | /**
107 | * Bottom padding
108 | * @member {Number}
109 | */
110 | get bottom() {
111 | return this._bottom;
112 | }
113 |
114 | set bottom(val) { // eslint-disable-line require-jsdoc
115 | this._bottom = val;
116 | this.emit('changed', this);
117 | }
118 | /**
119 | * Right padding
120 | * @member {Number}
121 | */
122 | get right() {
123 | return this._right;
124 | }
125 |
126 | set right(val) { // eslint-disable-line require-jsdoc
127 | this._right = val;
128 | this.emit('changed', this);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Point.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3';
2 |
3 | /**
4 | * Hold position data.
5 | * Like PIXI.Point but with certain events
6 | * @memberof ST
7 | * @extends external:EventEmitter
8 | */
9 | export default class Point extends EventEmitter {
10 | /**
11 | * @param {Number} [x=0] - x coordinate
12 | * @param {Number} [y=0] - y coordinate
13 | */
14 | constructor(x = 0, y = 0) {
15 | super();
16 |
17 | /**
18 | * Internal x coordinate
19 | * @member {Number}
20 | * @private
21 | */
22 | this._x = x;
23 |
24 | /**
25 | * Internal y coordinate
26 | * @member {Number}
27 | * @private
28 | */
29 | this._y = y;
30 |
31 | /**
32 | * Fires when positional data changes
33 | * @event ST.Point#changed
34 | * @param {ST.Point}
35 | */
36 | }
37 |
38 | /**
39 | * Convient way to set both coordinates
40 | * @param {Number} [x=0]
41 | * @param {Number} [y=0]
42 | */
43 | set(x = 0, y = 0) {
44 | this._x = x;
45 | this._y = y;
46 | this.emit('changed', this);
47 | }
48 |
49 | /**
50 | * The X coodinate
51 | * @member {Number}
52 | */
53 | get x() {
54 | return this._x;
55 | }
56 |
57 | set x(val) { // eslint-disable-line require-jsdoc
58 | this._x = val;
59 | this.emit('changed', this);
60 | }
61 |
62 | /**
63 | * The Y coodinate
64 | * @member {Number}
65 | */
66 | get y() {
67 | return this._y;
68 | }
69 |
70 | set y(val) { // eslint-disable-line require-jsdoc
71 | this._y = val;
72 | this.emit('changed', this);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Stores settings and default values used by the app and widgets.
3 | * Exports a settings object that adds the settings to itself?
4 | * Access like: ST.setting.slider.button.height
5 | * @memberof ST
6 | * @static
7 | */
8 | class Settings {
9 | /**
10 | *
11 | */
12 | constructor() {}
13 |
14 | /**
15 | * Adds settings to the global object;
16 | * @param {String} name Name of the settings
17 | * @param {Object} settings Settings to Adds
18 | */
19 | add(name, settings) {
20 | if(!name || !settings) {
21 | return;
22 | }
23 |
24 | this[name] = settings;
25 | }
26 | }
27 |
28 | let settings = new Settings();
29 | export {settings};
30 |
--------------------------------------------------------------------------------
/src/Size.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3';
2 |
3 | /**
4 | * Holds size information
5 | * @memberof ST
6 | * @extends external:EventEmitter
7 | */
8 | export default class Size extends EventEmitter {
9 | /**
10 | * @param {Number} [width=0]
11 | * @param {Number} [height=0]
12 | */
13 | constructor(width = 0, height = 0) {
14 | super();
15 |
16 | /**
17 | * Internal width
18 | * @member {Number}
19 | * @private
20 | */
21 | this._width = width;
22 |
23 | /**
24 | * Internal height
25 | * @member {Number}
26 | * @private
27 | */
28 | this._height = height;
29 |
30 | /**
31 | * Fires when size data changes
32 | * @event ST.Size#changed
33 | */
34 | }
35 |
36 | /**
37 | * Convienece to set both width and height at once
38 | * @param {Number} [width=0]
39 | * @param {Number} [height=0]
40 | */
41 | set(width = 0, height = 0) {
42 | this._width = width;
43 | this._height = height;
44 | this.emit('changed', this);
45 | }
46 |
47 | /**
48 | * @member {Number}
49 | */
50 | get width() {
51 | return this._width;
52 | }
53 |
54 | set width(val) { // eslint-disable-line require-jsdoc
55 | this._width = val;
56 | this.emit('changed', this);
57 | }
58 |
59 | /**
60 | * @member {Number}
61 | */
62 | get height() {
63 | return this._height;
64 | }
65 |
66 | set height(val) { // eslint-disable-line require-jsdoc
67 | this._height = val;
68 | this.emit('changed', this);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Theme.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3';
2 | import GraphicsGen from './GraphicsGen';
3 | import Point from './Point';
4 |
5 | /**
6 | * Themes represent colors and font properties used by the ui.
7 | * @see The example style sheets for reference
8 | * @memberof ST
9 | * @extends external:EventEmitter
10 | *
11 | * @example
12 | * // set only on color for panel
13 | * let myStyle = {
14 | * widgets:{
15 | * panel: {
16 | * enabled: 0xffffff,
17 | * },
18 | * },
19 | * };
20 | *
21 | * // Will use default styles except for panel.enabled
22 | * let thm = new ST.Theme(myStyle);
23 | *
24 | * // will change the theme for everything parented to root
25 | * app.theme = thm;
26 | *
27 | * // will change theme only for this widget
28 | * widget.theme = thm;
29 | */
30 | export default class Theme extends EventEmitter {
31 | /**
32 | * @param {Object} [options] Style sheet object
33 | */
34 | constructor(options = {}) {
35 | super();
36 | const opts = Object.assign(Theme.defaults, options);
37 |
38 | /**
39 | * Theme colors
40 | * @member {Object}
41 | */
42 | this.colors = JSON.parse(JSON.stringify(opts.widgets));
43 |
44 | /**
45 | * Theme font styles
46 | * @member {Object}
47 | */
48 | this.fontStyles = JSON.parse(JSON.stringify(opts.text));
49 |
50 | /**
51 | * Frame rects for theme textures
52 | * @member {Object}
53 | */
54 | this.frames = JSON.parse(JSON.stringify(this.colors));
55 |
56 | /**
57 | * The base texture for all the themes textures
58 | * @member {PIXI.BaseTexture}
59 | */
60 | this.baseTexture = this.makeTexture();
61 |
62 | /**
63 | * Theme textures(frames for the base texture)
64 | * @member {Object}
65 | */
66 | this.textures = Object.assign(this.frames, {});
67 |
68 | // Creates the textures from the frames
69 | this.makeTexturesRecursive(this.textures);
70 |
71 | /**
72 | * Renderer background
73 | * @member {Number}
74 | */
75 | this.background = opts.background;
76 | }
77 |
78 | /**
79 | * Returns the global graphic used by all widgets for clipping
80 | * @static
81 | * @return {PIXI.Graphics}
82 | */
83 | static getClipGraphic() {
84 | return Theme.clipGraphic;
85 | }
86 |
87 | /**
88 | * Adds the given styles to the global Theme.defaults.
89 | * @param {String} name Name of the widget
90 | * @param {Object} styles A collection of styles for the widget
91 | * @static
92 | */
93 | static registerDefaultWidgetStyle(name, styles) {
94 | if(!name || !styles) {
95 | return;
96 | }
97 | Theme.defaults.widgets[name] = styles;
98 | }
99 |
100 | /**
101 | *(For internal use only!)
102 | *Makes sure a graphic is created and parented to a base graphic
103 | *for every color listed in the widgets section of the theme.
104 | *graphics are positioned with 1 pixel of spacing.
105 | *@param {Object} frames containes widget section of theme object
106 | *@param {ST.Point} pos position object to be
107 | *continuous throughout recursion
108 | *@param {PIXI.Graphics} graphic base graphic
109 | *that theme graphics are added to
110 | *@private
111 | */
112 | makeGraphicsRecursive(frames, pos, graphic) {
113 | let keys = Object.keys(frames);
114 |
115 | for (let i = 0; i < keys.length; i++) {
116 | if(frames[keys[i]] instanceof PIXI.Rectangle) continue;
117 | if(typeof frames[keys[i]] === 'number') {
118 | let g = GraphicsGen
119 | .rectangleGraphic(4, 4, frames[keys[i]]);
120 | graphic.addChild(g);
121 | g.position.set(pos.x, pos.y);
122 | frames[keys[i]] = new PIXI.Rectangle(pos.x+1, pos.y+1, 1, 1);
123 | pos.x += 6;
124 | } else {
125 | this.makeGraphicsRecursive(frames[keys[i]], pos, graphic);
126 | }
127 | }
128 | }
129 |
130 | /**
131 | * Makes the base texture used to color the widgets
132 | * @private
133 | * @return {PIXI.Texture}
134 | */
135 | makeTexture() {
136 | let baseGraphic = GraphicsGen.rectangleGraphic(1024, 8, 0x000000);
137 | let pos = new Point();
138 |
139 | /* make this.frames = the widgets portion of the theme Object.
140 | The colors in this.frames will be replaced by PIXI.Rectangles
141 | that are the texture frames*/
142 | this.frames = JSON.parse(JSON.stringify(this.colors));
143 |
144 | this.makeGraphicsRecursive(this.frames, pos, baseGraphic);
145 |
146 | return baseGraphic.generateCanvasTexture().baseTexture;
147 | }
148 |
149 | /**
150 | * Makes all the textures needed by the theme. BaseTextures
151 | * contain the actual image. Textures serve as frames for the
152 | * sprites.
153 | * @param {PIXI.BaseTexture} tex
154 | * @private
155 | */
156 | makeTexturesRecursive(tex = this.textures) {
157 | let keys = Object.keys(tex);
158 |
159 | for(let i = 0; i < keys.length; i++) {
160 | if(tex[keys[i]] instanceof PIXI.Texture) continue;
161 | if(tex[keys[i]] instanceof PIXI.Rectangle) {
162 | tex[keys[i]] =
163 | new PIXI.Texture(this.baseTexture, tex[keys[i]]);
164 | } else {
165 | this.makeTexturesRecursive(tex[keys[i]]);
166 | }
167 | }
168 | }
169 | }
170 |
171 | // Global Theme defaults
172 | Theme.defaults = {
173 | background: 0x222222,
174 | widgets: {
175 |
176 | },
177 | text:
178 | {
179 | enabled: {
180 | fontFamily: 'Arial',
181 | fontSize: 12,
182 | fill: 0x959595,
183 | },
184 | disabled: {
185 | fontFamily: 'Arial',
186 | fontSize: 12,
187 | fill: 0x555555,
188 | },
189 | hover: {
190 | fontFamily: 'Arial',
191 | fontSize: 12,
192 | fill: 0x85ad85,
193 | },
194 | click: {
195 | fontFamily: 'Arial',
196 | fontSize: 12,
197 | fill: 0x99c799,
198 | },
199 | },
200 | };
201 |
202 | // Global clip graphic
203 | Theme.clipGraphic = GraphicsGen.rectangleGraphic(1, 1, 0x000000);
204 |
--------------------------------------------------------------------------------
/src/const.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {Number}
3 | * @memberof ST
4 | * @const
5 | * @readonly
6 | */
7 | export const HORIZONTAL = 0;
8 |
9 | /**
10 | * @type {Number}
11 | * @memberof ST
12 | * @const
13 | * @readonly
14 | */
15 | export const VERTICAL = 1;
16 |
--------------------------------------------------------------------------------
/src/demos/demos.js:
--------------------------------------------------------------------------------
1 | /*
2 | This demonstrates one way to create an app with
3 | SaberTooth by creating the app as a subclass of
4 | ST.App( prototypal inheritance ) and then
5 | instantiating it in index.html. For simple apps
6 | you could just as well use a script tag in index.html
7 | and just create instances of SaberTooth objects
8 | without subclassing.
9 |
10 | One could also install & use SaberTooth via npm.
11 | (npm install sabertooth --save-dev)
12 | */
13 |
14 | // Cocoon canvas+ doesn't like use strict ???
15 | // 'use strict';
16 |
17 | // SaberTooth should be loaded in index.html before this file.
18 | // Themes should be loaded in index.html before this file.
19 |
20 | // Create a subclass of ST.App
21 | var DemoApp = function(theme) { //eslint-disable-line
22 | // call parent constructor
23 | ST.App.call(this, {
24 | name: 'Demos',
25 | theme: theme, // must be loaded before this file
26 | autoResize: true,
27 | forceWebgl: true,
28 | });
29 |
30 | // hack the sprite renderer to get a draw count since PIXI.js no longer
31 | // supports draw counts.
32 | ST.hackSpriteRendererDrawCounter(this.renderer.plugins.sprite);
33 | this.root.layout = new ST.Layouts.VBoxLayout(this.root);
34 |
35 | this.createDebugPanel();
36 | this.createSliderPanel();
37 | };
38 |
39 | // Inherit from ST.App
40 | DemoApp.prototype = Object.create(ST.App.prototype);
41 | // Set constructor back to DemoApp
42 | DemoApp.prototype.constructor = DemoApp;
43 |
44 | // METHODS ===========================================
45 |
46 | // Begins the main application loop
47 | DemoApp.prototype.begin = function() {
48 | var _this = this; //eslint-disable-line
49 |
50 | this.main = function() {
51 | // reset the draw count for the next frame
52 | _this.renderer.plugins.sprite.draws = 0;
53 |
54 | _this.update(); // updates the app
55 |
56 | _this.dpDrawCountLabel.beginBypassUpdate(); // prevent a layout update
57 | _this.dpDrawCountLabel.text = _this.renderer.plugins.sprite.draws;
58 | _this.dpDrawCountLabel.endBypassUpdate();
59 |
60 | requestAnimationFrame(_this.main); // rinse, repeat
61 | };
62 |
63 | this.main();
64 | };
65 |
66 | // Creates a simple debug panel with draw counter.
67 | // TODO: add fps
68 | DemoApp.prototype.createDebugPanel = function() {
69 | // the panel itself with a vertical box layout
70 | this.debugPanel = new ST.Widgets.Panel(this.root, {
71 | width: 300,
72 | height: 150,
73 | });
74 | this.debugPanel.layout = new ST.Layouts.VBoxLayout(this.debugPanel);
75 |
76 | // creates a label at the top with the apps name
77 | this.dpTitle = new ST.Widgets.Label(this.debugPanel, {text: this.name});
78 |
79 | // creates a container with hbox layout below the title
80 | this.dpDrawCountSet
81 | = new ST.Widgets.Container(this.debugPanel, {height: 30});
82 | this.dpDrawCountSet.layout = new ST.Layouts.HBoxLayout(this.dpDrawCountSet);
83 | this.dpDrawCountSet.hPolicy
84 | = new ST.SizePolicies.ExpandingPolicy(this.dpDrawCountSet);
85 |
86 | // sets 'Draw Count: ' on the left side of this.dpDrawCountSet
87 | this.dpDrawCountTitle
88 | = new ST.Widgets.Label(this.dpDrawCountSet, {text: 'Draw Count: '});
89 |
90 | // set the draw count label to the right of 'Draw Count: '
91 | this.dpDrawCountLabel
92 | = new ST.Widgets.Label(this.dpDrawCountSet, {text: '0'});
93 | };
94 |
95 | DemoApp.prototype.createSliderPanel = function() {
96 | this.slPanel = new ST.Widgets.Panel(this.root, {
97 | width: 400,
98 | height: 100,
99 | x: 200,
100 | y: 200,
101 | });
102 | this.slPanel.layout = new ST.Layouts.VBoxLayout(this.slPanel);
103 |
104 | this.sl1 = new ST.Widgets.Slider(this.slPanel, {
105 | width: 300,
106 | height: 30,
107 | });
108 | this.sl1.hPolicy
109 | = new ST.SizePolicies.ExpandingPolicy(this.sl1);
110 |
111 | this.b1 = new ST.Widgets.TextButton(this.slPanel, {
112 | text: 'Button 1',
113 | width: 100,
114 | height: 30,
115 | });
116 |
117 | this.b2 = new ST.Widgets.TextButton(this.slPanel, {
118 | text: 'Another Button',
119 | width: 100,
120 | height: 30,
121 | });
122 |
123 | this.b3 = new ST.Widgets.TextButton(this.slPanel, {
124 | text: 'Button Three',
125 | width: 100,
126 | height: 100,
127 | });
128 | };
129 |
--------------------------------------------------------------------------------
/src/demos/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The EventEmitter namespace
3 | * @external EventEmitter
4 | * @see https://github.com/primus/eventemitter3
5 | */
6 |
7 | /**
8 | * The PIXI namespace
9 | * @external PIXI
10 | * @see http://pixijs.download/release/docs/index.html
11 | */
12 |
13 | /**
14 | * PIXI.TransformStatic
15 | * @external TransformStatic
16 | * @see http://pixijs.download/release/docs/PIXI.TransformStatic.html
17 | */
18 |
19 | /**
20 | * PIXI.Container
21 | * @external Container
22 | * @see http://pixijs.download/release/docs/PIXI.Container.html
23 | */
24 |
25 | import App from './App';
26 | import {HORIZONTAL, VERTICAL} from './const';
27 | import GraphicsGen from './GraphicsGen';
28 | import Padding from './Padding';
29 | import Point from './Point';
30 | import Size from './Size';
31 | import Theme from './Theme';
32 | import {hackSpriteRendererDrawCounter} from './drawcount';
33 | import {settings} from './Settings';
34 |
35 | import * as Widgets from './widgets';
36 |
37 | import {Layouts, SizePolicies, Alignment,
38 | LayoutManager, SizeManager} from './layoutSys';
39 |
40 | export {
41 | HORIZONTAL,
42 | VERTICAL,
43 | Alignment,
44 | Widgets,
45 | Layouts,
46 | SizePolicies,
47 | LayoutManager,
48 | SizeManager,
49 | GraphicsGen,
50 | Padding,
51 | Point,
52 | Size,
53 | Theme,
54 | App,
55 | hackSpriteRendererDrawCounter,
56 | settings,
57 | };
58 |
59 | global.ST = exports; // eslint-disable-line
60 |
--------------------------------------------------------------------------------
/src/layoutSys/Alignment.js:
--------------------------------------------------------------------------------
1 | import Point from '.././Point';
2 | import EventEmitter from 'eventemitter3';
3 |
4 | /**
5 | * Holds alignment policies for horizontal
6 | * and vertical alignments - alignments are static callback
7 | * methods that return a relative position offset for the widget to
8 | * its parent.
9 | * @memberof ST
10 | * @extends external:EventEmitter
11 | *
12 | * @example
13 | * let widget = new ST.Widgets.Button(myApp.root, {
14 | * width: 100,
15 | * height: 30,
16 | * });
17 | *
18 | * widget.layout = new ST.Layouts.VBoxLayout(widget);
19 | *
20 | * widget.layout.alignment.hAlign = ST.Alignment.right;
21 | * widget.layout.alignment.vAlign = ST.Alignment.middle;
22 | */
23 | export default class Alignment extends EventEmitter {
24 | /**
25 | * @param {Function} [hAlign=Alignment.left] Horizontal alignment
26 | * @param {Function} [vAlign=Alignment.top] Vertical alignment
27 | */
28 | constructor(hAlign = Alignment.left, vAlign = Alignment.top) {
29 | super();
30 | /**
31 | * Holds the HORIZONTAL alignment
32 | * @member {Function}
33 | */
34 | this.hAlign = hAlign;
35 |
36 | /**
37 | * Holds the VERTICAL alignment
38 | * @member {Function}
39 | */
40 | this.vAlign = vAlign;
41 | }
42 |
43 | /**
44 | * Get a position offset from alignments
45 | * @param {ST.Widgets.BaseWidget} parent The parent widget
46 | * @param {Number} targetWidth The widgets width
47 | * @param {Number} targetHeight The widgets height
48 | * @return {Point} The calculated offset relative to the parent
49 | */
50 | getOffset(parent, targetWidth, targetHeight) {
51 | let offset = new Point();
52 | offset.x = this.hAlign(targetWidth, parent);
53 | offset.y = this.vAlign(targetHeight, parent);
54 | return offset;
55 | }
56 |
57 | // ALIGNMENTS ---
58 |
59 | /**
60 | * Left Alignment
61 | * @static
62 | * @param {Number} targetWidth The widgets width
63 | * @param {ST.Widgets.BaseWidget} parent The parent widget
64 | * @return {Number} Horizontal offset
65 | */
66 | static left(targetWidth, parent) {
67 | return parent.padding.left;
68 | }
69 |
70 | /**
71 | * Center Alignment
72 | * @static
73 | * @param {Number} targetWidth The widgets width
74 | * @param {ST.Widgets.BaseWidget} parent The parent widget
75 | * @return {Number} Horizontal offset
76 | */
77 | static center(targetWidth, parent) {
78 | return ((parent.width / 2) - (targetWidth / 2));// - pad;
79 | }
80 |
81 | /**
82 | * Right Alignment
83 | * @static
84 | * @param {Number} targetWidth The widgets width
85 | * @param {ST.Widgets.BaseWidget} parent The parent widget
86 | * @return {Number} Horizontal offset
87 | */
88 | static right(targetWidth, parent) {
89 | return (parent.width - targetWidth);
90 | }
91 |
92 | /**
93 | * Top Alignment
94 | * @static
95 | * @param {Number} targetHeight The widgets width
96 | * @param {ST.Widgets.BaseWidget} parent The parent widget
97 | * @return {Number} Vertical offset
98 | */
99 | static top(targetHeight, parent) {
100 | return parent.padding.top;
101 | }
102 |
103 | /**
104 | * Middle Alignment
105 | * @static
106 | * @param {Number} targetHeight The widgets width
107 | * @param {ST.Widgets.BaseWidget} parent The parent widget
108 | * @return {Number} Vertical offset
109 | */
110 | static middle(targetHeight, parent) {
111 | return ((parent.height / 2) - (targetHeight / 2));// - pad;
112 | }
113 |
114 | /**
115 | * Bottom Alignment
116 | * @static
117 | * @param {Number} targetHeight The widgets width
118 | * @param {ST.Widgets.BaseWidget} parent The parent widget
119 | * @return {Number} Vertical offset
120 | */
121 | static bottom(targetHeight, parent) {
122 | return (parent.height - targetHeight);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/layoutSys/index.js:
--------------------------------------------------------------------------------
1 | import * as Layouts from './layouts';
2 | import * as SizePolicies from './sizePolicies';
3 | import Alignment from './Alignment';
4 |
5 | export{
6 | Layouts,
7 | SizePolicies,
8 | Alignment,
9 | };
10 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/BaseLayout.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3';
2 | import Alignment from '.././Alignment';
3 | import BaseWidget from '../../widgets/BaseWidget';
4 |
5 | /**
6 | * The BaseLayout class is the abstract base class for all layouts.
7 | * @memberof ST.Layouts
8 | * @abstract
9 | */
10 | export default class BaseLayout extends EventEmitter {
11 | /**
12 | * @param {ST.Widgets.BaseWidget} hostWidget
13 | * - Widget this layout belongs to
14 | */
15 | constructor(hostWidget) {
16 | super();
17 | // make sure we have a host widget
18 | if(!hostWidget) {
19 | throw new Error('Layouts must be created ' +
20 | 'with a valid host widget parameter');
21 | }
22 |
23 | /**
24 | * The internal reference to the host widget
25 | * @member {ST.Widgets.BaseWidget}
26 | * @private
27 | */
28 | this._host = hostWidget;
29 |
30 | /**
31 | * Holds the Alignment object
32 | * @member {ST.Alignment}
33 | */
34 | this.alignment = new Alignment();
35 |
36 | /**
37 | * If set to true then the layout will update on host widget
38 | * changes in position and size.
39 | * @member {Boolean}
40 | * @default false
41 | */
42 | this.updateOnHostChanges = false;
43 |
44 | /**
45 | * Fired when layout has finished updating
46 | * @event ST.Layouts.BaseLayout#updated
47 | */
48 | }
49 |
50 | /**
51 | *Override in layouts if they need to act upon the addition or
52 | *removal of children from the host widget.
53 | *@virtual
54 | *@private
55 | */
56 | hostChildrenChanged() {}
57 |
58 | /**
59 | * Overide in layouts - code that runs before the iteration
60 | * of child layouts
61 | * @virtual
62 | * @private
63 | */
64 | beginIteration() {}
65 |
66 | /**
67 | * Overide in layouts - code that runs after the iteration
68 | * of child layouts
69 | * @virtual
70 | * @private
71 | */
72 | endIteration() {}
73 |
74 | /**
75 | * Overide in layouts - code that sets the position of
76 | * each child widget.
77 | * @param {ST.Widgets.BaseWidget} child the child widget in question
78 | * @virtual
79 | * @private
80 | */
81 | setChildPos(child) {}
82 |
83 | /**
84 | * Execute the layout update - iterates over child widgets
85 | * and executes their layouts before positioning them.
86 | * @private
87 | */
88 | exec() {
89 | this.beginIteration();
90 | const w = this._host;
91 | const len = w.children.length;
92 | let i = len;
93 | while(i--) {
94 | const child = w.children[len - i]; // iterate in forward order
95 | if(child instanceof BaseWidget) {
96 | child.beginBypassUpdate(); // prevents infinite loop
97 |
98 | this.setChildPos(child);
99 | child.layout.exec();
100 |
101 | child.endBypassUpdate();
102 | }
103 | }
104 |
105 | /* layouts execute after size policies therefor
106 | update is complete
107 | */
108 | w.validate(); // make sure updates stop on next loop
109 | w._updateClipGraphic(); // because things have changed
110 |
111 | w.emit('updated');
112 |
113 | this.endIteration();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/BoxLayout.js:
--------------------------------------------------------------------------------
1 | import BaseLayout from './BaseLayout';
2 | import Point from '../.././Point';
3 | import {HORIZONTAL} from '../.././const';
4 | import BaseWidget from '../.././widgets/BaseWidget';
5 |
6 | /**
7 | * Arrange out widgets in a straight line in one direction
8 | * @memberof ST.Layouts
9 | * @extends ST.Layouts.BaseLayout
10 | *
11 | * @example
12 | * let widget = new ST.Widgets.Button(myApp.root, {
13 | * width: 100,
14 | * height: 30,
15 | * });
16 | *
17 | * widget.layout = new ST.Layouts.BoxLayout(widget, ST.HORIZONTAL);
18 | */
19 | export default class BoxLayout extends BaseLayout {
20 | /**
21 | *@param {ST.Widgets.BaseWidget} hostWidget
22 | *The widget this layout belongs to
23 | *@param {number} [orientation=HORIZONTAL] Orientation of layout
24 | *@param {number} [spacing=4] the space between widgets in pixels
25 | */
26 | constructor(hostWidget, orientation = HORIZONTAL, spacing = 4) {
27 | super(hostWidget);
28 |
29 | /**
30 | * Orientation of the layout
31 | * @member {number}
32 | */
33 | this.orientation = orientation;
34 |
35 | /**
36 | * The space between widgets in pixels
37 | * @member {number}
38 | */
39 | this.spacing = spacing;
40 |
41 | /**
42 | * Holds the offset applied to each
43 | * consecutive child widget's position
44 | * @member {Point}
45 | * @private
46 | */
47 | this.posOffset = new Point();
48 |
49 | /**
50 | * Total width of child widgets combined
51 | * @member {Number}
52 | * @private
53 | */
54 | this._totalChildrenWidth = 0;
55 |
56 | /**
57 | * Total height of child widgets combined
58 | * @member {Number}
59 | * @private
60 | */
61 | this._totalChildrenHeight = 0;
62 |
63 | /**
64 | * Result of the old calculation. This is the value used.
65 | * @member {Number}
66 | * @private
67 | */
68 | this._totalChildrenWidthOld = 0;
69 |
70 | /**
71 | * Result of the old calculation. This is the value used.
72 | * @member {Number}
73 | * @private
74 | */
75 | this._totalChildrenHeightOld = 0;
76 |
77 | /**
78 | * Wether child sizes have been calculted
79 | * @member {Boolean}
80 | * @private
81 | */
82 | this._totalChildrenSizeInitialized = false;
83 |
84 | /**
85 | * Fires when layout is finished
86 | * @event ST.Layouts.BoxLayout#finished
87 | */
88 | }
89 |
90 | /**
91 | * Calculates the total combined size of children
92 | * @private
93 | */
94 | initTotalChildrenSize() {
95 | const children = this._host.children;
96 | let i = children.length;
97 | while(i--) {
98 | const child = children[i];
99 | if(child instanceof BaseWidget) {
100 | this._totalChildrenWidth += child.width + this.spacing;
101 | this._totalChildrenHeight += child.height + this.spacing;
102 | }
103 | }
104 |
105 | this._totalChildrenSizeInitialized = true;
106 | }
107 |
108 | /**
109 | * Prepare for iteration
110 | * @override
111 | * @private
112 | */
113 | beginIteration() {
114 | if(!this._totalChildrenSizeInitialized) {
115 | this.initTotalChildrenSize();
116 | };
117 | // cache the old sizes
118 | this._totalChildrenWidthOld = this._totalChildrenWidth;
119 | this._totalChildrenHeightOld = this._totalChildrenHeight;
120 |
121 | // reset totals
122 | this._totalChildrenWidth = 0;
123 | this._totalChildrenHeight = 0;
124 |
125 | this.posOffset.set(0, 0);
126 | }
127 |
128 | /**
129 | * Sets childs position and calculate next offset
130 | * @param {ST.Widgets.BaseWidget} child - the child in question
131 | * @override
132 | */
133 | setChildPos(child) {
134 | let alignmentOffset;
135 |
136 | // update should already be bypassed on host so this
137 | // shouldn't cause an update / infinite loop
138 | child.position.set(this.posOffset.x, this.posOffset.y);
139 |
140 | // add spacing to the totals for the next one
141 | this._totalChildrenWidth += child.width + this.spacing;
142 | this._totalChildrenHeight += child.height + this.spacing;
143 |
144 | if(this.orientation === HORIZONTAL) {
145 | alignmentOffset = this.alignment.getOffset(this._host,
146 | this._totalChildrenWidthOld, child.height);
147 |
148 | // set posOffset for the next one
149 | this.posOffset.x += child.width + this.spacing;
150 | } else { // VERTICAL
151 | alignmentOffset = this.alignment.getOffset(this._host,
152 | child.width, this._totalChildrenHeightOld);
153 |
154 | // set posOffset for the next one
155 | this.posOffset.y += child.height + this.spacing;
156 | }
157 |
158 | child.x += alignmentOffset.x;
159 | child.y += alignmentOffset.y;
160 |
161 | // set the real position from the virtual
162 | child.applyPosition();
163 | }
164 |
165 | /**
166 | * Emits finished event
167 | * @override
168 | */
169 | endIteration() {
170 | this.emit('finished');
171 | }
172 |
173 | /**
174 | * When host widgets children change this will re-calculate the
175 | * total size of the children
176 | * @override
177 | */
178 | hostChildrenChanged() {
179 | this._totalChildrenSizeInitialized = false;
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/FixedLayout.js:
--------------------------------------------------------------------------------
1 | import BaseLayout from './BaseLayout';
2 |
3 | /**
4 | * Arranges widgets according to their user defined position.
5 | * @memberof ST.Layouts
6 | * @extends BaseLayout
7 | *
8 | * @example
9 | * let widget = new ST.Widgets.Button(myApp.root, {
10 | * width: 100,
11 | * height: 30,
12 | * });
13 | *
14 | * widget.layout = new ST.Layouts.FixedLayout(widget);
15 | */
16 | export default class FixedLayout extends BaseLayout {
17 | /**
18 | * @param {ST.Widgets.BaseWidget} hostWidget
19 | * The widget this layout belongs to
20 | */
21 | constructor(hostWidget) {
22 | super(hostWidget);
23 |
24 | this.updateOnHostChanges = true;
25 | }
26 |
27 |
28 | /**
29 | * Sets the widgets position with offset
30 | * @param {ST.Widgets.BaseWidget} child - the child in question
31 | * @override
32 | */
33 | setChildPos(child) {
34 | const alignOffset = this.alignment.getOffset(this._host,
35 | child.width, child.height);
36 |
37 | child.applyPosition(alignOffset.x, alignOffset.y);
38 | }
39 |
40 | /** @inheritdoc */
41 | endIteration() {
42 | this.emit('finished');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/HBoxLayout.js:
--------------------------------------------------------------------------------
1 | import BoxLayout from './BoxLayout';
2 | import {HORIZONTAL} from '../.././const';
3 |
4 | /**
5 | * Arranges widgets horizontally stacked beside each other.
6 | * (Same as box layout with orientation = ST.HORIZONTAL)
7 | * @memberof ST.Layouts
8 | * @extends ST.Layouts.BoxLayout
9 | *
10 | * @example
11 | * let widget = new ST.Widgets.Button(myApp.root, {
12 | * width: 100,
13 | * height: 30,
14 | * });
15 | *
16 | * widget.layout = new ST.Layouts.HBoxLayout(widget);
17 | */
18 | export default class HBoxLayout extends BoxLayout {
19 | /**
20 | * @param {ST.Widgets.BaseWidget} hostWidget
21 | * The widget this layout belongs to
22 | * @param {Number} [spacing=4] The space in pixels between widgets
23 | */
24 | constructor(hostWidget, spacing = 4) {
25 | super(hostWidget, HORIZONTAL, spacing);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/VBoxLayout.js:
--------------------------------------------------------------------------------
1 | import BoxLayout from './BoxLayout';
2 | import {VERTICAL} from '../.././const';
3 |
4 | /**
5 | * Arranges widgets vertically stacked on top of each other.
6 | * (Same as box layout with orientation = ST.VERTICAL)
7 | * @memberof ST.Layouts
8 | * @extends ST.Layouts.BoxLayout
9 | *
10 | * @example
11 | * let widget = new ST.Widgets.Button(myApp.root, {
12 | * width: 100,
13 | * height: 30,
14 | * });
15 | *
16 | * widget.layout = new ST.Layouts.VBoxLayout(widget);
17 | */
18 | export default class VBoxLayout extends BoxLayout {
19 | /**
20 | * @param {ST.Widgets.BaseWidget} hostWidget
21 | * The widget this layout belongs to
22 | * @param {Number} [spacing=4] The space in pixels between widgets
23 | */
24 | constructor(hostWidget, spacing = 4) {
25 | super(hostWidget, VERTICAL, spacing);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/layoutSys/layouts/index.js:
--------------------------------------------------------------------------------
1 | import BaseLayout from './BaseLayout';
2 | import FixedLayout from './FixedLayout';
3 | import BoxLayout from './BoxLayout';
4 | import HBoxLayout from './HBoxLayout';
5 | import VBoxLayout from './VBoxLayout';
6 |
7 | export {
8 | BaseLayout,
9 | FixedLayout,
10 | BoxLayout,
11 | HBoxLayout,
12 | VBoxLayout,
13 | };
14 |
--------------------------------------------------------------------------------
/src/layoutSys/sizePolicies/BasePolicy.js:
--------------------------------------------------------------------------------
1 | import BaseWidget from '../../widgets/BaseWidget';
2 | import EventEmitter from 'eventemitter3';
3 | import {HORIZONTAL} from '../.././const';
4 |
5 | /**
6 | * Abstract base class for all size policies
7 | * @memberof ST.SizePolicies
8 | * @abstract
9 | */
10 | export default class BasePolicy extends EventEmitter {
11 | /**
12 | * @param {ST.Widgets.BaseWidget} hostWidget
13 | * The widget this policy belongs to
14 | * @param {number} [orientation=HORIZONTAL] The orientation of the policy
15 | */
16 | constructor(hostWidget, orientation = HORIZONTAL) {
17 | super();
18 | // make sure we have a host
19 | if(!hostWidget) {
20 | throw new Error('SizePolicy must be created ' +
21 | 'with a valid host widget parameter');
22 | }
23 |
24 | /**
25 | * The orientation of the policy
26 | * @member {number}
27 | */
28 | this.orientation = orientation;
29 |
30 | /**
31 | * The widget this policy belongs to
32 | * @member {ST.Widgets.BaseWidget}
33 | * @private
34 | */
35 | this._host = hostWidget;
36 |
37 | /**
38 | * Combined size of child widgets who have finished
39 | * executing their size policy
40 | * @member {Number}
41 | * @private
42 | */
43 | this.totalChildrenFinishedSize = 0;
44 |
45 | /**
46 | * Count of child widgets who have finished
47 | * executing their size policy
48 | * @member {Number}
49 | * @private
50 | */
51 | this.totalChildrenFinished = 0;
52 |
53 | /**
54 | * If set to true the policy will update on changes to
55 | * the parent widgets size and position
56 | * @member {Boolean}
57 | * @default false
58 | * @private
59 | */
60 | this.updateOnHostChanges = false;
61 |
62 | /**
63 | * Total space used by children widgets
64 | * @member {Number}
65 | * @private
66 | */
67 | this.usedSpace = 0;
68 |
69 | /**
70 | * Keeps track of the spacing in the current layout
71 | * @member {Number}
72 | * @private
73 | */
74 | this.layoutSpacing = 0;
75 |
76 | /**
77 | * Fires when iteration of children begins
78 | * @event ST.SizePolicies.BasePolicy#preIteration
79 | */
80 |
81 | /**
82 | * Fires when iteration of children ends
83 | * @event ST.SizePolicies.BasePolicy#postIteration
84 | */
85 | }
86 |
87 | /**
88 | * Handles sizing the widget if HORIZONTAL orientation
89 | * @virtual
90 | */
91 | sizeWidgetHorizontal() {}
92 |
93 | /**
94 | * Handles sizing the widget if VERTICAL orientation
95 | * @virtual
96 | */
97 | sizeWidgetVertical() {}
98 |
99 | /**
100 | * As each child widgets policy finishes we count them and
101 | * add their sizes to the collective
102 | * @param {number} size - Size of the child passed in
103 | * @private
104 | */
105 | childPolicyFinished(size = 0) {
106 | this.totalChildrenFinishedSize += size + this.layoutSpacing;
107 | this.usedSpace += size + this.layoutSpacing;
108 | this.totalChildrenFinished -= 1;
109 | }
110 |
111 | /**
112 | * Execute the policy update - iterate over and execute
113 | * children widgets policies.
114 | * @private
115 | */
116 | exec() {
117 | this.emit('preIteration');
118 | let w = this._host;
119 | let len = w.children.length;
120 | let i = len;
121 | // because some layouts don't have spacing (FixedLayout)
122 | if('spacing' in this._host.layout) {
123 | this.layoutSpacing = this._host.layout.spacing;
124 | }
125 | this.usedSpace = 0 - this.layoutSpacing;
126 | // counts down instead of up
127 | this.totalChildrenFinished = len - 1;
128 | this.totalChildrenFinishedSize = 0 - this.layoutSpacing;
129 |
130 | while(i--) {
131 | const child = w.children[i];
132 | if(this.orientation === HORIZONTAL) {
133 | if(child instanceof BaseWidget) {
134 | // prevents infinite loop caused by updating each loop
135 | child.beginBypassUpdate();
136 | child.hPolicy.once('finished',
137 | this.childPolicyFinished, this);
138 | child.hPolicy.exec();
139 | child.endBypassUpdate();
140 | }
141 | } else {
142 | if(child instanceof BaseWidget) {
143 | // prevents infinite loop caused by updating each loop
144 | child.beginBypassUpdate();
145 | child.vPolicy.once('finished',
146 | this.childPolicyFinished, this);
147 | child.vPolicy.exec();
148 | child.endBypassUpdate();
149 | }
150 | }
151 | }
152 |
153 |
154 | if(this.orientation === HORIZONTAL) {
155 | this.sizeWidgetHorizontal();
156 | } else { // VERTICAL
157 | this.sizeWidgetVertical();
158 | }
159 |
160 | this.emit('postIteration');
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/layoutSys/sizePolicies/ExpandingPolicy.js:
--------------------------------------------------------------------------------
1 | import BasePolicy from './BasePolicy';
2 | import {HORIZONTAL} from '../.././const';
3 |
4 | /**
5 | * Expanding Policy causes children to expand in the given direction
6 | * filling the parent.
7 | * This Policy doesn't respect siblings - best for widgets with
8 | * a sigle child, or for example the hPolicy of a widget in a vertical layout.
9 | * @memberof ST.SizePolicies
10 | * @extends ST.SizePolicies.BasePolicy
11 | *
12 | * @example
13 | * let widget = new ST.Widgets.Button(myApp.root, {
14 | * width: 100,
15 | * height: 30,
16 | * });
17 | *
18 | * // will fill the entire parent regardless of sibling widgets
19 | * widget.hPolicy = new ST.SizePolicies.ExpandingPolicy(widget, ST.HORIZONTAL);
20 | * widget.vPolicy = new ST.SizePolicies.ExpandingPolicy(widget, ST.VERTICAL);
21 | */
22 | export default class ExpandingPolicy extends BasePolicy {
23 | /**
24 | * @param {ST.Widgets.BaseWidget} hostWidget
25 | * The widget this policy belongs to
26 | * @param {number} [orientation=HORIZONTAL] The orientation of the policy
27 | */
28 | constructor(hostWidget, orientation = HORIZONTAL) {
29 | super(hostWidget, orientation);
30 |
31 | this.updateOnHostChanges = false;
32 |
33 | /**
34 | * Fires after size is set
35 | * @event ST.SizePolicies.ExpandingPolicy#finished
36 | * @param {Number} size the size of the widget
37 | */
38 | }
39 |
40 | /**
41 | * Make connection so that the widget
42 | * only gets sized after the parent is sized.
43 | * @override
44 | */
45 | sizeWidgetHorizontal() {
46 | // connect slot each iteration because they are use once
47 | // this prevents problems when host widget changes parent
48 | if(this._host.parent) {
49 | this._host.parent.hPolicy.once('finished',
50 | this.parentReadyH, this);
51 | }
52 | }
53 |
54 | /**
55 | * Make connection so that the widget
56 | * only gets sized after the parent is sized.
57 | * @override
58 | */
59 | sizeWidgetVertical() {
60 | if(this._host.parent) {
61 | this._host.parent.vPolicy.once('finished',
62 | this.parentReadyV, this);
63 | }
64 | }
65 |
66 | /**
67 | * Size widget after parent widget is finished.
68 | * When parent is finished we can size the widget and
69 | * know that parents size wont change afterward.
70 | */
71 | parentReadyH() {
72 | const w = this._host;
73 | const p = this._host.parent;
74 | const pad = p.padding.left + p.padding.right;
75 |
76 | w.width = p.width - pad;
77 | w.validateWidth(); // make it obey widet.min and max
78 | this.emit('finished', w.width);
79 | }
80 |
81 | /**
82 | * Size widget after parent widget is finished.
83 | * When parent is finished we can size the widget and
84 | * know that parents size wont change afterward.
85 | */
86 | parentReadyV() {
87 | const w = this._host;
88 | const p = this._host.parent;
89 | const pad = p.padding.top + p.padding.bottom;
90 |
91 | w.height = p.height - pad;
92 | w.validateHeight(); // make it obey widet.min and max
93 | this.emit('finished', w.height);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/layoutSys/sizePolicies/FixedPolicy.js:
--------------------------------------------------------------------------------
1 | import {HORIZONTAL} from '../.././const';
2 | import BasePolicy from './BasePolicy';
3 |
4 | /**
5 | * Sets the user defined size
6 | * @memberof ST.SizePolicies
7 | * @extends ST.SizePolicies.BasePolicy
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.Button(myApp.root, {
11 | * width: 100,
12 | * height: 30,
13 | * });
14 | *
15 | * widget.hPolicy = new ST.SizePolicies.FixedPolicy(widget, ST.HORIZONTAL);
16 | * widget.vPolicy = new ST.SizePolicies.FixedPolicy(widget, ST.VERTICAL);
17 | */
18 | export default class FixedPolicy extends BasePolicy {
19 | /**
20 | * @param {ST.Widgets.BaseWidget} hostWidget
21 | * The widget this policy belongs to
22 | * @param {number} [orientation=HORIZONTAL] The orientation of the policy
23 | */
24 | constructor(hostWidget, orientation = HORIZONTAL) {
25 | super(hostWidget, orientation);
26 |
27 | this.updateOnHostChanges = true;
28 |
29 | /**
30 | * Fires after size is set
31 | * @event ST.SizePolicies.FixedPolicy#finished
32 | * @param {Number} size the size of the widget
33 | */
34 | }
35 |
36 | /**
37 | * Validates size
38 | * @override
39 | */
40 | sizeWidgetHorizontal() {
41 | this._host.validateWidth(); // make obey widget.min and max
42 | this.emit('finished', this._host.width);
43 | }
44 |
45 | /**
46 | * Validates size
47 | * @override
48 | */
49 | sizeWidgetVertical() {
50 | this._host.validateHeight(); // make obey widget.min and max
51 | this.emit('finished', this._host.height);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/layoutSys/sizePolicies/SharedExpandingPolicy.js:
--------------------------------------------------------------------------------
1 | import ExpandingPolicy from './ExpandingPolicy';
2 | import {HORIZONTAL, VERTICAL} from '../.././const';
3 |
4 | /**
5 | * Expands widgets into their parent while sharing space with their siblings.
6 | * @memberof ST.SizePolicies
7 | * @extends ST.SizePolicies.ExpandingPolicy
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.Button(myApp.root, {
11 | * width: 100,
12 | * height: 30,
13 | * });
14 | *
15 | * widget.hPolicy = new ST.SizePolicies
16 | * .SharedExpandingPolicy(widget, ST.HORIZONTAL);
17 | *
18 | * widget.vPolicy = new ST.SizePolicies
19 | * .SharedExpandingPolicy(widget, ST.VERTICAL);
20 | */
21 | export default class SharedExpandingPolicy extends ExpandingPolicy {
22 | /**
23 | * @param {ST.Widgets.BaseWidget} hostWidget
24 | * The widget this policy belongs to
25 | * @param {number} [orientation=HORIZONTAL] The orientation of the policy
26 | */
27 | constructor(hostWidget, orientation = HORIZONTAL) {
28 | super(hostWidget, orientation);
29 |
30 | /**
31 | * Fires after size has been set
32 | * @event ST.SizePolicies.SharedExpandingPolicy#finished
33 | * @param {Number} size the size of the widget
34 | */
35 | }
36 |
37 | /**
38 | * Size widget after parent widget is finished.
39 | * Note: When parent is finished we can size the widget and
40 | * know that parents size wont change afterward.
41 | * @override
42 | */
43 | parentReadyH() {
44 | const w = this._host;
45 | const parent = w.parent;
46 | const pad = parent.padding.left + parent.padding.right;
47 | const p = parent.hPolicy;
48 | const remaining = p.totalChildrenFinished - 1;
49 | const availableSpace
50 | = (parent.width - p.totalChildrenFinishedSize) - pad;
51 | let size;
52 |
53 | // because some layouts dont need an orientation
54 | if('orientation' in parent.layout &&
55 | parent.layout.orientation === VERTICAL) {
56 | size = parent.width - pad;
57 | w.width = size;
58 | w.validateWidth(); // obey widget min and max size
59 | } else {
60 | size = availableSpace / remaining;
61 | w.width = size;
62 | // obey widget min and max size
63 | let vWidth = w.validateWidth();
64 | if(vWidth === w.width) {
65 | p.once('postIteration', this.consumeUnusedSpaceH, this);
66 | }
67 | }
68 |
69 | this.emit('finished', w.width);
70 | }
71 |
72 | /**
73 | * Size widget after parent widget is finished.
74 | * Note: When parent is finished we can size the widget and
75 | * know that parents size wont change afterward.
76 | * @override
77 | */
78 | parentReadyV() {
79 | const w = this._host;
80 | const parent = w.parent;
81 | const pad = parent.padding.top + parent.padding.bottom;
82 | const p = parent.vPolicy;
83 | const remaining = p.totalChildrenFinished - 1;
84 | const availableSpace =
85 | (parent.height - p.totalChildrenFinishedSize ) - pad;
86 | let size;
87 |
88 | // because some layouts dont need an orientation
89 | if('orientation' in parent.layout &&
90 | parent.layout.orientation === HORIZONTAL) {
91 | size = parent.height - pad;
92 | w.height = size;
93 | w.validateHeight(); // obey widget min and max size
94 | } else {
95 | size = availableSpace / remaining;
96 | w.height = size;
97 | // obey widget min and max size
98 | let vHeight = w.validateHeight();
99 | if(vHeight === w.height) {
100 | p.once('postIteration', this.consumeUnusedSpaceV, this);
101 | }
102 | }
103 | this.emit('finished', w.height);
104 | }
105 |
106 | /**
107 | * Make sure widget gets its share of unused space after all of siblings
108 | * have been sized
109 | * @callback
110 | */
111 | consumeUnusedSpaceH() {
112 | const w = this._host;
113 | const parent = w.parent;
114 | const unusedSpace = parent.width - parent.hPolicy.usedSpace;
115 |
116 | // every widget listens to the postIteration event so use that
117 | // to get a count on remaining widgets
118 | const remaining = parent.hPolicy.listeners('postIteration')
119 | .filter((value)=>{
120 | return value.name === this.consumeUnusedSpaceH.name;
121 | });
122 | const pad = parent.padding.left + parent.padding.right;
123 | const relSize = (unusedSpace / (remaining.length+1)) - pad;
124 |
125 | // set the newly adjusted size
126 | w.width += relSize;
127 | w.validateWidth(); // obey widget min and max size
128 |
129 | // add used space back
130 | parent.hPolicy.usedSpace += relSize;
131 | }
132 |
133 | /**
134 | * Make sure widget gets its share of unused space after all of siblings
135 | * have been sized
136 | * @callback
137 | */
138 | consumeUnusedSpaceV() {
139 | const w = this._host;
140 | const parent = w.parent;
141 | const unusedSpace = parent.height - parent.vPolicy.usedSpace;
142 |
143 | // every widget listens to the postIteration event so use that
144 | // to get a count on remaining widgets
145 | const remaining = parent.vPolicy.listeners('postIteration')
146 | .filter((value)=>{
147 | return value.name === this.consumeUnusedSpaceV.name;
148 | });
149 | const pad = parent.padding.top + parent.padding.bottom;
150 | const relSize = (unusedSpace / (remaining.length+1)) - pad;
151 |
152 | // set the newly adjusted size
153 | w.height += relSize;
154 | w.validateHeight();// obey widget min and max size
155 |
156 | // add used space back
157 | parent.vPolicy.usedSpace += relSize;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/layoutSys/sizePolicies/index.js:
--------------------------------------------------------------------------------
1 | import BasePolicy from './BasePolicy';
2 | import FixedPolicy from './FixedPolicy';
3 | import ExpandingPolicy from './ExpandingPolicy';
4 | import SharedExpandingPolicy from './SharedExpandingPolicy';
5 |
6 | export {
7 | BasePolicy,
8 | FixedPolicy,
9 | ExpandingPolicy,
10 | SharedExpandingPolicy,
11 | };
12 |
--------------------------------------------------------------------------------
/src/widgets/Button.js:
--------------------------------------------------------------------------------
1 | import Panel from './Panel';
2 | import Alignment from '../layoutSys/Alignment';
3 | import Theme from '.././Theme';
4 |
5 | /* Add widget style to ST.Theme.defaults. This way the widget
6 | will always have a style even if the given theme doesn't have one
7 | specifically for it. All widgets that have themable elements
8 | should call this method before describing their class.*/
9 | Theme.registerDefaultWidgetStyle('button', {
10 | enabled: 0x363636,
11 | disabled: 0x2e2e2e,
12 | hover: 0x264e26,
13 | click: 0x3a723a,
14 | });
15 |
16 | /**
17 | * A simple button widget that is the base class for other button types.
18 | * @extends ST.Widgets.Panel
19 | * @memberof ST.Widgets
20 | *
21 | * @example
22 | * let widget = new ST.Widgets.Button(myApp.root, {
23 | * width: 100,
24 | * height: 30,
25 | * });
26 | */
27 | export default class Button extends Panel {
28 | /**
29 | * @param {ST.Widgets.BaseWidget} parent The widgets parent
30 | * @param {Object} [options = Object] See {@link ST.Widgets.BaseWidget}
31 | */
32 | constructor(parent, options = {}) {
33 | super(parent, options);
34 |
35 | this.interactive = true;
36 |
37 | // make alignment default to center
38 | this.layout.alignment.vAlign = Alignment.middle;
39 | this.layout.alignment.hAlign = Alignment.center;
40 | }
41 |
42 | /** @inheritdoc */
43 | paintDefault() {
44 | if(this._bkgObj) {
45 | this._bkgObj.texture = this.theme.textures.button.enabled;
46 | }
47 | }
48 |
49 | /** @inheritdoc */
50 | paintDown() {
51 | if(this._bkgObj) {
52 | this._bkgObj.texture = this.theme.textures.button.click;
53 | }
54 | }
55 |
56 | /** @inheritdoc */
57 | paintHover() {
58 | if(this._bkgObj) {
59 | this._bkgObj.texture = this.theme.textures.button.hover;
60 | }
61 | }
62 |
63 | /** @inheritdoc */
64 | paintDisabled() {
65 | if(this._bkgObj) {
66 | this._bkgObj.texture = this.theme.textures.button.disabled;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/widgets/Container.js:
--------------------------------------------------------------------------------
1 | import Panel from './Panel';
2 |
3 | /**
4 | * Serves as a container for other widgets. Can be used to layout
5 | * widgets without having a background.
6 | * @extends ST.Widgets.Panel
7 | * @memberof ST.Widgets
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.Container(myApp.root, {
11 | * width: 100,
12 | * height: 30,
13 | * });
14 | */
15 | export default class Container extends Panel {
16 | /**
17 | * @param {ST.Widgets.BaseWidget} parent parent widget
18 | * @param {Object} [options = Object] See {@link ST.Widgets.BaseWidget}
19 | */
20 | constructor(parent, options = {}) {
21 | super(parent, options);
22 | // Containers have nothing to interact with
23 | this.interactive = false;
24 | this.sizeProxy.renderable = false;
25 | this.padding.setAllTo(0);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/widgets/Image.js:
--------------------------------------------------------------------------------
1 | import BaseWidget from './BaseWidget';
2 |
3 | /**
4 | * Wraps PIXI.Sprite and allows images/ textures to become widgets
5 | * @extends ST.Widgets.BaseWidget
6 | * @memberof ST.Widgets
7 | *
8 | * @example
9 | * // load texture with PIXI first, then...
10 | *
11 | * let widget = new ST.Widgets.Image(myApp.root, {texture: myTexture});
12 | */
13 | export default class Image extends BaseWidget {
14 | /**
15 | *@param {ST.Widgets.BaseWidget} parent Widgets parent
16 | *@param {Object} [options = Object] See {@link ST.Widgets.BaseWidget}
17 | *@param {PIXI.Texture} [options.texture = null] The texture for the Image
18 | */
19 | constructor(parent, options = {}) {
20 | super(parent, options);
21 | // default options
22 | const defaults = {
23 | texture: null,
24 | };
25 |
26 | // fill in missing options with defaults
27 | options = Object.assign(defaults, options);
28 |
29 | // Images could be interactive but most will not
30 | this.interactive = false;
31 |
32 | /**
33 | * Holds the sprite internally
34 | * @member {PIXI.Sprite}
35 | * @private
36 | */
37 | this._sprite = new PIXI.Sprite();
38 | if(options.texture) {
39 | this._sprite.texture = options.texture;
40 | }
41 | this.addChild(this._sprite);
42 | this._sprite.width = this.width;
43 | this._sprite.height = this.height;
44 |
45 | this.sizeProxy = this._sprite;
46 | }
47 |
48 | /**
49 | * The PIXI.Sprite used internally
50 | * @member {PIXI.Sprite}
51 | */
52 | get sprite() {
53 | return this._sprite;
54 | }
55 |
56 | set sprite(val) { // eslint-disable-line require-jsdoc
57 | if(val instanceof PIXI.Sprite) {
58 | this._sprite = val;
59 | }
60 | }
61 |
62 | /**
63 | *The sprites texture
64 | *@member {PIXI.Texture}
65 | */
66 | get texture() {
67 | return this._sprite.texture;
68 | }
69 |
70 | set texture(val) { // eslint-disable-line require-jsdoc
71 | if(val instanceof PIXI.Texture) {
72 | this._sprite.texture = val;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/widgets/Label.js:
--------------------------------------------------------------------------------
1 | import * as PIXI from 'pixi.js';
2 | import BaseWidget from './BaseWidget';
3 |
4 | /**
5 | * A simple text label
6 | * @memberof ST.Widgets
7 | * @extends ST.Widgets.BaseWidget
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.Label(myApp.root, {text: 'My Text'});
11 | */
12 | export default class Label extends BaseWidget {
13 | /**
14 | * @param {ST.BaseWidget} parent Widgets parent
15 | * @param {Object} [options = Object] See {@link ST.Widgets.BaseWidget}
16 | * @param {String} [options.text] The text presented on the label
17 | */
18 | constructor(parent, options = {}) {
19 | super(parent, options);
20 | // default options
21 | const defaults = {
22 | text: '',
23 | };
24 |
25 | options = Object.assign(defaults, options);
26 |
27 | // this is mostly un-needed for labels
28 | this.interactive = false;
29 |
30 | /**
31 | * Internal PIXI.Text object
32 | * @member {PIXI.Text}
33 | * @private
34 | */
35 | this._textObj = new PIXI.Text();
36 | this.addChild(this._textObj);
37 | this._textObj.mask = null;
38 | this._clipGraphic.renderable = false;
39 | this.paintDefault();
40 | this.text = options.text;
41 |
42 | /**
43 | * Fires when the text is changed
44 | * @event ST.Widgets.Label#textChanged
45 | */
46 | }
47 |
48 | /** @inheritdoc */
49 | paintDefault() {
50 | if(this._textObj) {
51 | this._textObj.style = this.theme.fontStyles.enabled;
52 | }
53 | }
54 |
55 | /** @inheritdoc */
56 | paintDown() {
57 | if(this._textObj) {
58 | this._textObj.style = this.theme.fontStyles.click;
59 | }
60 | }
61 |
62 | /** @inheritdoc */
63 | paintHover() {
64 | if(this._textObj) {
65 | this._textObj.style = this.theme.fontStyles.hover;
66 | }
67 | }
68 |
69 | /** @inheritdoc */
70 | paintDisabled() {
71 | if(this._textObj) {
72 | this._textObj.style = this.theme.fontStyles.disabled;
73 | }
74 | }
75 |
76 | /**
77 | * The text presented by the label
78 | * @member {String}
79 | */
80 | get text() {
81 | return this._textObj.text;
82 | }
83 |
84 | set text(val) { // eslint-disable-line require-jsdoc
85 | this._textObj.text = val;
86 | this.width = this._textObj.width;
87 | this.height = this._textObj.height;
88 | this._updateClipGraphic();
89 | this.emit('textChanged', val);
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/widgets/Panel.js:
--------------------------------------------------------------------------------
1 | import BaseWidget from './BaseWidget';
2 | import Theme from '.././Theme';
3 |
4 | /* Add widget style to ST.Theme.defaults. This way the widget
5 | will always have a style even if the given theme doesn't have one
6 | specifically for it. All widgets that have themable elements
7 | should call this method before describing their class.*/
8 | Theme.registerDefaultWidgetStyle('panel', {
9 | enabled: 0x2b2b2b,
10 | disabled: 0x272727,
11 | hover: 0x244c24,
12 | click: 0x387038,
13 | });
14 |
15 | /**
16 | * Panels are simple rectangle widgets that contain other widgets.
17 | * @memberof ST.Widgets
18 | * @extends ST.Widgets.BaseWidget
19 | *
20 | * @example
21 | * let widget = new ST.Widgets.Panel(myApp.root, {
22 | * width: 400,
23 | * height: 400,
24 | * });
25 | */
26 | export default class Panel extends BaseWidget {
27 | /**
28 | * @param {ST.Widgets.BaseWidget} parent The widgets parent
29 | * @param {Object} [options] See {@link ST.Widgets.BaseWidget}
30 | */
31 | constructor(parent, options) {
32 | super(parent, options);
33 |
34 | // Most panels will be static, so interactivity is not needed
35 | // Remember to set true for subclasses that are.
36 | this.interactive = false;
37 |
38 | /**
39 | * Internal background sprite
40 | * @member {PIXI.Sprite}
41 | * @private
42 | */
43 | this._bkgObj = new PIXI.Sprite(this.theme.texture);
44 | this.addChild(this._bkgObj);
45 | this._bkgObj.width = this.width;
46 | this._bkgObj.height = this.height;
47 |
48 | this.sizeProxy = this._bkgObj;
49 |
50 | this.paintDefault();
51 | }
52 |
53 | /** @inheritdoc */
54 | paintDefault() {
55 | if(this._bkgObj) {
56 | this._bkgObj.texture = this.theme.textures.panel.enabled;
57 | }
58 | }
59 |
60 | /** @inheritdoc */
61 | paintDisabled() {
62 | if(this._bkgObj) {
63 | this._bkgObj.texture = this.theme.textures.panel.disabled;
64 | }
65 | }
66 |
67 | /** @inheritdoc */
68 | paintHover() {
69 | if(this._bkgObj) {
70 | this._bkgObj.texture = this.theme.textures.panel.hover;
71 | }
72 | }
73 |
74 | /** @inheritdoc */
75 | paintDown() {
76 | if(this._bkgObj) {
77 | this._bkgObj.texture = this.theme.textures.panel.click;
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/widgets/SITransform.js:
--------------------------------------------------------------------------------
1 | import * as PIXI from 'pixi.js';
2 |
3 | /**
4 | * Transform that makes a PIXI.Container size independent
5 | * from its parent. ( FOR INTERNAL USE ONLY! )
6 | * @memberof ST.Widgets
7 | * @extends external:TransformStatic
8 | */
9 | export default class SITransform extends PIXI.TransformStatic {
10 | /**
11 | *
12 | */
13 | constructor() {
14 | super();
15 | }
16 |
17 | /**
18 | * @inheritDoc
19 | */
20 | updateTransform(parentTransform) {
21 | const lt = this.localTransform;
22 | if (this._localID !== this._currentLocalID) {
23 | // get the matrix values of the displayobject
24 | // based on its transform properties..
25 | lt.a = this._cx * this.scale._x;
26 | lt.b = this._sx * this.scale._x;
27 | lt.c = this._cy * this.scale._y;
28 | lt.d = this._sy * this.scale._y;
29 | lt.tx = this.position._x - ((this.pivot._x * lt.a) +
30 | (this.pivot._y * lt.c));
31 | lt.ty = this.position._y - ((this.pivot._x * lt.b) +
32 | (this.pivot._y * lt.d));
33 | this._currentLocalID = this._localID;
34 | // force an update..
35 | this._parentID = -1;
36 | }
37 | if (this._parentID !== parentTransform._worldID) {
38 | // concat the parent matrix with the objects transform.
39 | const pt = parentTransform.worldTransform;
40 | const wt = this.worldTransform;
41 |
42 | /* I believe that removing the folowing prevent size modification
43 | from the parent*/
44 |
45 | // wt.a = (lt.a * pt.a) + (lt.b * pt.c);
46 | // wt.b = (lt.a * pt.b) + (lt.b * pt.d);
47 | // wt.c = (lt.c * pt.a) + (lt.d * pt.c);
48 | // wt.d = (lt.c * pt.b) + (lt.d * pt.d);
49 | //
50 | wt.tx = (lt.tx * pt.a) + (lt.ty * pt.c) + pt.tx;
51 | wt.ty = (lt.tx * pt.b) + (lt.ty * pt.d) + pt.ty;
52 | this._parentID = parentTransform._worldID;
53 | // update the id of the transform..
54 | this._worldID ++;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/widgets/StageWidget.js:
--------------------------------------------------------------------------------
1 | import BaseWidget from './BaseWidget';
2 |
3 | /**
4 | * Container widget that keeps a fixed width and height. Probably should only
5 | * be used for full screen widgets such as the apps root widget.
6 | * @memberof ST.Widgets
7 | * @extends ST.Widgets.BaseWidget
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.StageWidget(myApp.root, {
11 | * width: 100,
12 | * height: 300,
13 | * });
14 | */
15 | export default class StageWidget extends BaseWidget {
16 | /**
17 | * @param {ST.Widgets.BaseWidget} parent The widgets parent
18 | * @param {Object} [options] See {@link ST.Widgets.BaseWidget}
19 | */
20 | constructor(parent, options = {}) {
21 | super(parent, options);
22 | // Nothing to interact with
23 | this.interactive = false;
24 | }
25 |
26 | /**
27 | * Calculate bounds to remain at the user
28 | * defined width and height of the widget as it
29 | * count the user defined size into the bounds.
30 | * @private
31 | * @override
32 | */
33 | _calculateBounds() {
34 | this._bounds.minX = 0;
35 | this._bounds.minY = 0;
36 | this._bounds.maxX = this._width;
37 | this._bounds.maxY = this._height;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/widgets/TextButton.js:
--------------------------------------------------------------------------------
1 | import Button from './Button';
2 | import Label from './Label';
3 |
4 | /**
5 | * Button with a text label
6 | * @memberof ST.Widgets
7 | * @extends ST.Widgets.Button
8 | *
9 | * @example
10 | * let widget = new ST.Widgets.TextButton(myApp.root, {
11 | * width: 100,
12 | * height: 30,
13 | * text: 'My Text Button',
14 | * });
15 | */
16 | export default class TextButton extends Button {
17 | /**
18 | * @param {ST.BaseWidget} parent The widgets parent
19 | * @param {Object} [options] See {@link ST.Widgets.BaseWidget}
20 | * @param {String} [options.text] The labels text
21 | */
22 | constructor(parent, options = {}) {
23 | super(parent, options);
24 |
25 | const defaults = {
26 | text: '',
27 | };
28 |
29 | options = Object.assign(defaults, options);
30 |
31 | /**
32 | * The internal label
33 | * @member {ST.Widgets.Label}
34 | * @private
35 | */
36 | this.label = new Label(this, {text: options.text});
37 | }
38 |
39 | /**
40 | * The buttons text
41 | * @member {String}
42 | */
43 | get text() {
44 | return this.label.text;
45 | }
46 |
47 | set text(val) { // eslint-disable-line require-jsdoc
48 | this.label.text = val;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/widgets/index.js:
--------------------------------------------------------------------------------
1 | import BaseWidget from './BaseWidget';
2 | import StageWidget from './StageWidget';
3 | import Label from './Label';
4 | import Panel from './Panel';
5 | import Button from './Button';
6 | import Slider from './Slider';
7 | import TextButton from './TextButton';
8 | import SITransform from './SITransform';
9 | import Container from './Container';
10 | import Image from './Image';
11 |
12 | export {
13 | BaseWidget,
14 | StageWidget,
15 | Label,
16 | Panel,
17 | Button,
18 | Slider,
19 | TextButton,
20 | SITransform,
21 | Container,
22 | Image,
23 | };
24 |
--------------------------------------------------------------------------------
/styleSheets/greyToadTheme.js:
--------------------------------------------------------------------------------
1 | greyToadTheme = {
2 | background: 0x333333,
3 | widgets: {
4 | panel: {
5 | enabled: 0x2b2b2b,
6 | disabled: 0x272727,
7 | hover: 0x244c24,
8 | click: 0x387038,
9 | },
10 | button: {
11 | enabled: 0x363636,
12 | disabled: 0x2e2e2e,
13 | hover: 0x264e26,
14 | click: 0x3a723a,
15 | },
16 | slider: {
17 | track: {
18 | enabled: 0x303030,
19 | disabled: 0x2e2e2e,
20 | },
21 | button: {
22 | enabled: 0x284328,
23 | disabled: 0x303030,
24 | hover: 0x264e26,
25 | click: 0x3a723a,
26 | },
27 | },
28 | },
29 | text: {
30 | enabled: {
31 | fontFamily: 'Arial',
32 | fontSize: 12,
33 | fill: 0x959595,
34 | },
35 | disabled: {
36 | fontFamily: 'Arial',
37 | fontSize: 12,
38 | fill: 0x555555,
39 | },
40 | hover: {
41 | fontFamily: 'Arial',
42 | fontSize: 12,
43 | fill: 0x85ad85,
44 | },
45 | click: {
46 | fontFamily: 'Arial',
47 | fontSize: 12,
48 | fill: 0x99c799,
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/styleSheets/holyStyle.js:
--------------------------------------------------------------------------------
1 | holyStyle = {
2 | background: 0xffffff,
3 | widgets: {
4 | panel: {
5 | enabled: 0xffffff,
6 | disabled: 0xffffff,
7 | hover: 0xffffff,
8 | click: 0xffffff,
9 | },
10 | button: {
11 | enabled: 0xbdbdbd,
12 | disabled: 0xb4b4b4,
13 | hover: 0xb20000,
14 | click: 0xc00000,
15 | },
16 | slider: {
17 | track: {
18 | enabled: 0xb4b4b4,
19 | disabled: 0xb4b4b4,
20 | },
21 | button: {
22 | enabled: 0xb20000,
23 | disabled: 0xbdbdbd,
24 | hover: 0xbb0000,
25 | click: 0xc00000,
26 | },
27 | },
28 | },
29 | text: {
30 | enabled: {
31 | fontFamily: 'Arial',
32 | fontSize: 12,
33 | fill: 0x000000,
34 | },
35 | disabled: {
36 | fontFamily: 'Arial',
37 | fontSize: 12,
38 | fill: 0xe5e5e5,
39 | },
40 | hover: {
41 | fontFamily: 'Arial',
42 | fontSize: 12,
43 | fill: 0xc00000,
44 | },
45 | click: {
46 | fontFamily: 'Arial',
47 | fontSize: 12,
48 | fill: 0xc00000,
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('ST', ()=>{
4 | it('should exist as a global object', ()=>{
5 | expect(ST).to.be.an('object');
6 | });
7 | require('./spec');
8 | });
9 |
--------------------------------------------------------------------------------
/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mocha
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
24 |
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/spec/App-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('App', ()=>{
4 | let app = new ST.App();
5 |
6 | it('should create a pixi canvas on the page', ()=>{
7 | expect(document.body.contains(app.renderer.view)).to.be.true;
8 | });
9 |
10 | it('should have a valid theme', ()=>{
11 | expect(app.theme).to.be.an.instanceof(ST.Theme);
12 | });
13 |
14 | it('should have a valid root widget', ()=>{
15 | expect(app.root).to.be.an.instanceof(ST.Widgets.BaseWidget);
16 | });
17 |
18 | it('should resize the root widget to match the window', ()=>{
19 | window.resizeBy(300, 200);
20 | expect(app.root.width).to.equal(window.innerWidth);
21 | expect(app.root.height).to.equal(window.innerHeight);
22 | });
23 |
24 | describe('#name', ()=>{
25 | it('should change the page title', ()=>{
26 | app.name = 'SaberTooth Test';
27 | expect(document.title).to.equal('SaberTooth Test');
28 | });
29 | });
30 |
31 | describe('#autoResize', ()=>{
32 | it('should add a listener to resize event if set to true and ' +
33 | 'one doesnt already exist', ()=>{
34 | app.autoResize = true;
35 | let listeners = app.listeners('resize');
36 | expect(listeners.indexOf(app.resizeToWindow)).to.not.equal(-1);
37 | });
38 |
39 | it('should not add more than one listener', ()=>{
40 | app.autoResize = true;
41 | let listeners = app.listeners('resize');
42 | expect(listeners.length).to.equal(1);
43 | });
44 |
45 | it('should remove the listener from resize if set to false', ()=>{
46 | app.autoResize = false;
47 | let listeners = app.listeners('resize');
48 | expect(listeners.indexOf(app.resizeToWindow)).to.equal(-1);
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/spec/GraphicsGen-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('GraphicsGen', ()=>{
4 | describe('#rectangleGraphic()', ()=>{
5 | it('should return a Pixi.Graphics with a '
6 | + 'width of 80 and a height of 100', ()=>{
7 | let rectGraphic =
8 | ST.GraphicsGen.rectangleGraphic(80, 100, 0x000000);
9 |
10 | expect(rectGraphic).to.be.an.instanceof(PIXI.Graphics);
11 | expect(rectGraphic.width).to.equal(80);
12 | expect(rectGraphic.height).to.equal(100);
13 | });
14 | });
15 |
16 | describe('#rectangleTexture()', ()=>{
17 | it('should return a PIXI.Texture with a'
18 | + ' width of 100 and a height of 80', ()=>{
19 | let rectTex =
20 | ST.GraphicsGen.rectangleTexture(100, 80, 0x000000);
21 |
22 | expect(rectTex).to.be.an.instanceof(PIXI.Texture);
23 | expect(rectTex.width).to.equal(100);
24 | expect(rectTex.height).to.equal(80);
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/spec/Padding-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Padding', ()=>{
4 | let pad = new ST.Padding();
5 | it('should be created with a default of 0', ()=>{
6 | expect(pad.left).to.equal(0);
7 | expect(pad.top).to.equal(0);
8 | expect(pad.right).to.equal(0);
9 | expect(pad.bottom).to.equal(0);
10 | });
11 |
12 | describe('#set()', ()=>{
13 | it('should set each padding to the values of 1,2,3,4', ()=>{
14 | pad.set(1, 2, 3, 4);
15 | expect(pad.left).to.equal(2);
16 | expect(pad.top).to.equal(1);
17 | expect(pad.right).to.equal(4);
18 | expect(pad.bottom).to.equal(3);
19 | });
20 | });
21 |
22 | describe('#setAllTo()', ()=>{
23 | it('should set all padding values to 10', ()=>{
24 | pad.setAllTo(10);
25 | expect(pad.left).to.equal(10);
26 | expect(pad.top).to.equal(10);
27 | expect(pad.right).to.equal(10);
28 | expect(pad.bottom).to.equal(10);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/spec/Point-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Point', ()=>{
4 | let p = new ST.Point();
5 | it('should create with default x and y of 0', ()=>{
6 | expect(p.x).to.equal(0);
7 | expect(p.y).to.equal(0);
8 | });
9 | describe('#set()', ()=>{
10 | it('should set x and y to 20, 80', ()=>{
11 | p.set(20, 80);
12 | expect(p.x).to.equal(20);
13 | expect(p.y).to.equal(80);
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/spec/Settings-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Settings', ()=>{
4 | it('should have a global instance', ()=>{
5 | expect(ST.settings).to.not.be.unidentified;
6 | });
7 |
8 | describe('#add()', ()=>{
9 | it('should add the given object as a property of itself', ()=>{
10 | let obj = {
11 | truck: {
12 | size: 'big',
13 | weight: 'heavy',
14 | },
15 | };
16 |
17 | ST.settings.add('vehicles', obj);
18 |
19 | expect(ST.settings.vehicles.truck.size).to.equal('big');
20 | expect(ST.settings.vehicles.truck.weight).to.equal('heavy');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/spec/Size-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Size', ()=>{
4 | let s = new ST.Size();
5 | it('should create with default size of 0', ()=>{
6 | expect(s.width).to.equal(0);
7 | expect(s.height).to.equal(0);
8 | });
9 | describe('#set()', ()=>{
10 | it('should set width and height to 20, 80', ()=>{
11 | s.set(20, 80);
12 | expect(s.width).to.equal(20);
13 | expect(s.height).to.equal(80);
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/spec/Theme-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Theme', ()=>{
4 | // let greyToadTheme = require('../../themes/greyToadTheme');
5 | // let change one style because this theme is the same as the defaults
6 | greyToadTheme.background = 0xffffff;
7 | greyToadTheme.widgets.button.hover = 0xff00ff;
8 | let theme = new ST.Theme(greyToadTheme);
9 | let defTheme = new ST.Theme();
10 |
11 | it('should load the given styles when created', ()=>{
12 | expect(theme.background).to.equal(0xffffff);
13 | expect(theme.colors.button.hover).to.equal(0xff00ff);
14 | // not gonna check each one. This should be enough
15 | });
16 |
17 | it('should load the default styles if no style is given', ()=>{
18 | expect(defTheme.background).to.equal(ST.Theme.defaults.background);
19 | expect(defTheme.colors.button.hover).to.equal(ST.Theme.defaults
20 | .widgets.button.hover);
21 | // not gonna check each one. This should be enough
22 | });
23 |
24 | describe('#colors', ()=>{
25 | it('should equal the widgets portion of the styles', ()=>{
26 | expect(theme.colors).to.deep.equal(greyToadTheme.widgets);
27 | });
28 | });
29 |
30 | describe('#fontStyles', ()=>{
31 | it('should equal the text portion of the styles', ()=>{
32 | expect(theme.fontStyles).to.deep.equal(greyToadTheme.text);
33 | });
34 | });
35 |
36 | describe('#frames', ()=>{
37 | it('should contain rects in the structure of the widgets' +
38 | ' portion of the styles', ()=>{
39 | // textures becomes an alias for frames therefor
40 | // see #textures
41 |
42 | // try a few of them
43 | // expect(theme.frames.button.enabled).to.be.an
44 | // .instanceof(PIXI.Rectangle);
45 | // expect(theme.frames.panel.hover).to.be.an
46 | // .instanceof(PIXI.Rectangle);
47 | // expect(theme.frames.slider.button.enabled).to.be.an
48 | // .instanceof(PIXI.Rectangle);
49 | });
50 | });
51 |
52 | describe('#baseTexture', ()=>{
53 | it('should be a PIXI.BaseTexture', ()=>{
54 | expect(theme.baseTexture).to.be.an.instanceof(PIXI.BaseTexture);
55 | });
56 | });
57 |
58 | describe('#textures', ()=>{
59 | it('should contain textures in the structure of the widgets' +
60 | ' portion of the styles', ()=>{
61 | // try a few of them
62 | expect(theme.textures.button.enabled).to.be.an
63 | .instanceof(PIXI.Texture);
64 | expect(theme.textures.panel.hover).to.be.an
65 | .instanceof(PIXI.Texture);
66 | expect(theme.textures.slider.button.enabled).to.be.an
67 | .instanceof(PIXI.Texture);
68 | });
69 | });
70 |
71 | describe('#background', ()=>{
72 | it('should equal the color given in the style', ()=>{
73 | expect(theme.background).to.equal(0xffffff);
74 | });
75 | });
76 |
77 | describe('#getClipGraphic() (STATIC)', ()=>{
78 | it('should return the global clipGraphic', ()=>{
79 | expect(ST.Theme.getClipGraphic()).to.be.an
80 | .instanceof(PIXI.Graphics);
81 | });
82 | });
83 |
84 | describe('#registerDefaultWidgetStyle() (STATIC)', ()=>{
85 | it('should add the given style to theme.defaults(global)', ()=>{
86 | let style = {
87 | knob: {
88 | enabled: 0x000000,
89 | disabled: 0xffffff,
90 | },
91 | };
92 |
93 | ST.Theme.registerDefaultWidgetStyle('pane', style);
94 |
95 | expect(ST.Theme.defaults.widgets.pane.knob.enabled)
96 | .to.equal(0x000000);
97 |
98 | expect(ST.Theme.defaults.widgets.pane.knob.disabled)
99 | .to.equal(0xffffff);
100 | });
101 | });
102 |
103 | describe('#makeGraphicsRecursive()', ()=>{
104 | // not needed if frames test above passes
105 | });
106 |
107 | describe('#makeTexture()', ()=>{
108 | // not needed if baseTexture test above passes
109 | });
110 |
111 | describe('#makeTexturesRecursive()', ()=>{
112 | // not needed if textures text above passes
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/test/spec/index.js:
--------------------------------------------------------------------------------
1 | require('./Theme-spec');
2 | require('./Size-spec');
3 | require('./Point-spec');
4 | require('./Padding-spec');
5 | require('./GraphicsGen-spec');
6 | require('./App-spec');
7 | require('./widgets');
8 | require('./layoutSys');
9 | require('./Settings-spec');
10 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/Alignment-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Alignment', ()=>{
4 | let widget0 = new ST.Widgets.BaseWidget();
5 | widget0.width = 400; widget0.height = 400;
6 | let widget1 = new ST.Widgets.BaseWidget(widget0);
7 | widget1.width = 200; widget1.height = 200;
8 |
9 | describe('#getOffset()', ()=>{
10 | it('should return a relative offset base on the given horizontal'
11 | + ' and vertical alignment', ()=>{
12 | widget0.layout.alignment.hAlign = ST.Alignment.center;
13 | widget0.layout.alignment.vAlign = ST.Alignment.middle;
14 | let off = new ST.Point();
15 | off = widget0.layout.alignment.getOffset(widget0,
16 | widget1.width, widget1.height);
17 | // (widget0.width/2) - (widget1.width/2)
18 | expect(off.x).to.equal(100);
19 | expect(off.y).to.equal(100);
20 | });
21 | });
22 |
23 | describe('#left', ()=>{
24 | it('should return relative position for left alignment', ()=>{
25 | let off = ST.Alignment.left(400, widget0);
26 | expect(off).to.equal(4);
27 | });
28 | });
29 |
30 | describe('#center', ()=>{
31 | it('should return relative position for center alignment', ()=>{
32 | let off = ST.Alignment.center(100, widget0);
33 | expect(off).to.equal(150);
34 | });
35 | });
36 |
37 | describe('#right', ()=>{
38 | it('should return relative position for right alignment', ()=>{
39 | let off = ST.Alignment.right(100, widget0);
40 | expect(off).to.equal(300);
41 | });
42 | });
43 |
44 | describe('#top', ()=>{
45 | it('should return relative position for top alignment', ()=>{
46 | let off = ST.Alignment.top(100, widget0);
47 | expect(off).to.equal(4);
48 | });
49 | });
50 |
51 | describe('#middle', ()=>{
52 | it('should return relative position for middle alignment', ()=>{
53 | let off = ST.Alignment.middle(100, widget0);
54 | expect(off).to.equal(150);
55 | });
56 | });
57 |
58 | describe('#bottom', ()=>{
59 | it('should return relative position for bottom alignment', ()=>{
60 | let off = ST.Alignment.bottom(100, widget0);
61 | expect(off).to.equal(300);
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/index.js:
--------------------------------------------------------------------------------
1 | require('./Alignment-spec');
2 | require('./sizePolicies');
3 | require('./layouts');
4 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/BaseLayout-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('BaseLayout', ()=>{
4 | let widget0 = new ST.Widgets.Panel(null, {width: 100, height: 100});
5 | let widget1 = new ST.Widgets.Button(widget0, {width: 20, height: 20});
6 | widget1.position.set(20, 20);
7 |
8 | describe('#exec', ()=>{
9 | it('should set the position of its children and exec their layours',
10 | ()=>{
11 | // real position isnt set until layout executes
12 | expect(widget1.transform.position.x).to.equal(0);
13 | expect(widget1.transform.position.y).to.equal(0);
14 |
15 | let spy = sinon.spy(widget1.layout, 'exec');
16 |
17 | widget0.layout.exec();
18 |
19 | expect(spy.called).to.be.true;
20 |
21 | widget1.layout.exec.restore();
22 |
23 | expect(widget1.transform.position.x).to.equal(24);
24 | expect(widget1.transform.position.y).to.equal(24);
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/BoxLayout-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('BoxLayout', ()=>{
4 | let w0 = new ST.Widgets.Panel(null, {width: 400, height: 400});
5 | w0.layout = new ST.Layouts.BoxLayout(w0, ST.VERTICAL);
6 | let w1 = new ST.Widgets.Button(w0, {width: 20, height: 20});
7 | let w2 = new ST.Widgets.Button(w0, {width: 20, height: 20}); // eslint-disable-line
8 | let w3 = new ST.Widgets.Button(w0, {width: 20, height: 20}); // eslint-disable-line
9 |
10 | describe('#initTotalChildrenSize()', ()=>{
11 | it('should calculate the total size of the children', ()=>{
12 | expect(w0.layout._totalChildrenWidth).to.equal(0);
13 | expect(w0.layout._totalChildrenHeight).to.equal(0);
14 |
15 | w0.layout.initTotalChildrenSize();
16 |
17 | // add together + spacing
18 | expect(w0.layout._totalChildrenWidth).to.equal(72);
19 | expect(w0.layout._totalChildrenHeight).to.equal(72);
20 | });
21 | });
22 |
23 | describe('#beginIteration()', ()=>{
24 | it('should prepare variables before iteration', ()=>{
25 | let wLayout = w0.layout;
26 | wLayout.beginIteration();
27 |
28 | expect(wLayout._totalChildrenHeight).to.equal(0);
29 | expect(wLayout._totalChildrenWidth).to.equal(0);
30 |
31 | expect(wLayout.posOffset.x).to.equal(0);
32 | expect(wLayout.posOffset.y).to.equal(0);
33 | });
34 | });
35 |
36 | describe('#setChildPos()', ()=>{
37 | it('should set the position of the child', ()=>{
38 | let wLayout = w0.layout;
39 | wLayout.setChildPos(w1);
40 |
41 | expect(wLayout._totalChildrenWidth).to.equal(24);
42 | expect(wLayout._totalChildrenHeight).to.equal(24);
43 |
44 | expect(w1.transform.position.x).to.equal(4);
45 | expect(w1.transform.position.y).to.equal(4);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/FixedLayout-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('FixedLayout', ()=>{
4 | let w0 = new ST.Widgets.Panel(null, {width: 400, height: 400});
5 | let w1 = new ST.Widgets.Button(w0);
6 | w1.position.set(12, 12);
7 |
8 | describe('#setChildPos()', ()=>{
9 | it('should set the childs position', ()=>{
10 | expect(w1.transform.position.x).to.equal(0);
11 | expect(w1.transform.position.y).to.equal(0);
12 |
13 | w0.layout.setChildPos(w1);
14 |
15 | expect(w1.transform.position.x).to.equal(16);
16 | expect(w1.transform.position.y).to.equal(16);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/HBoxLayout-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('HBoxLayout', ()=>{
4 | it('should construct a BoxLayout with HORIZONTAL orientation', ()=>{
5 | let w1 = new ST.Widgets.Panel();
6 |
7 | w1.layout = new ST.Layouts.HBoxLayout(w1);
8 | expect(w1.layout.orientation).to.equal(ST.HORIZONTAL);
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/VBoxLayout-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('VBoxLayout', ()=>{
4 | it('should construct a BoxLayout with VERTICAL orientation', ()=>{
5 | let w1 = new ST.Widgets.Panel();
6 |
7 | w1.layout = new ST.Layouts.VBoxLayout(w1);
8 | expect(w1.layout.orientation).to.equal(ST.VERTICAL);
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/layouts/index.js:
--------------------------------------------------------------------------------
1 | require('./BaseLayout-spec');
2 | require('./BoxLayout-spec');
3 | require('./FixedLayout-spec');
4 | require('./HBoxLayout-spec');
5 | require('./VBoxLayout-spec');
6 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/sizePolicies/BasePolicy-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('BasePolicy', ()=>{
4 | let widget = new ST.Widgets.Button();
5 |
6 | describe('#childPolicyFinished()', ()=>{
7 | it('should add each childs size to totalChildrenFinishedSize and'
8 | + ' substract 1 from totalChildrenFinished', ()=>{
9 | widget.hPolicy.totalChildrenFinished = 1;
10 | widget.hPolicy.totalChildrenFinishedSize = 100;
11 | widget.hPolicy.childPolicyFinished(400);
12 |
13 | expect(widget.hPolicy.totalChildrenFinished).to.equal(0);
14 | expect(widget.hPolicy.totalChildrenFinishedSize).to.equal(500);
15 | });
16 | });
17 |
18 | describe('#exec()', ()=>{
19 | let widget2 = new ST.Widgets.Button(widget);
20 |
21 | it('should add a one time event'
22 | + '( register size with childPolicyFinished ) and exec the '
23 | + 'HORIZONTAL size policy for each child '
24 | + 'that is HORIZONTAL and an instanceof ST.Widgets.BaseWidget', ()=>{
25 | let spy = sinon.spy(widget2.hPolicy, 'exec');
26 | expect(widget2.hPolicy.listeners('finished', true)).to.be.false;
27 | expect(widget2).to.be.an.instanceof(ST.Widgets.BaseWidget);
28 | expect(widget.hPolicy.orientation).to.equal(ST.HORIZONTAL);
29 | widget.hPolicy.exec();
30 |
31 | expect(spy.called).to.be.true;
32 | widget2.hPolicy.exec.restore();
33 | });
34 |
35 | it('should add a one time event'
36 | + '( register size with childPolicyFinished ) and exec the '
37 | + 'VERTICAL size policy for each child '
38 | + 'that is VERTICAL and an instanceof ST.Widgets.BaseWidget', ()=>{
39 | let spy = sinon.spy(widget2.vPolicy, 'exec');
40 | expect(widget2.vPolicy.listeners('finished', true)).to.be.false;
41 | expect(widget2).to.be.an.instanceof(ST.Widgets.BaseWidget);
42 | expect(widget.vPolicy.orientation).to.equal(ST.VERTICAL);
43 | widget.vPolicy.exec();
44 |
45 | expect(spy.called).to.be.true;
46 | widget2.vPolicy.exec.restore();
47 | });
48 |
49 | it('should call sizeWidgetHorizontal() if the widget is HORIZONTAL',
50 | ()=>{
51 | let spy = sinon.spy(widget.hPolicy, 'sizeWidgetHorizontal');
52 | widget.hPolicy.exec();
53 | expect(spy.called).to.be.true;
54 | widget.hPolicy.sizeWidgetHorizontal.restore();
55 | });
56 |
57 | it('should call sizeWidgetVertical() if the widget is VERTICAL',
58 | ()=>{
59 | let spy = sinon.spy(widget.vPolicy, 'sizeWidgetVertical');
60 | widget.vPolicy.exec();
61 | expect(spy.called).to.be.true;
62 | widget.vPolicy.sizeWidgetVertical.restore();
63 | });
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/sizePolicies/ExpandingPolicy-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('ExpandingPolicy', ()=>{
4 | let widget0 = new ST.Widgets.Button();
5 | widget0.width = 200; widget0.height = 200;
6 | let widget1 = new ST.Widgets.Button(widget0);
7 | widget1.hPolicy = new ST.SizePolicies.ExpandingPolicy(widget1);
8 | widget1.vPolicy = new ST.SizePolicies.ExpandingPolicy(widget1);
9 | widget1.width = 100; widget1.height = 100;
10 |
11 | describe('#parentReadyH', ()=>{
12 | it('should size the widget to match its parents width - padding', ()=>{
13 | expect(widget1.width).to.equal(100);
14 | widget1.hPolicy.parentReadyH();
15 | expect(widget1.width).to.equal(192);
16 | });
17 | });
18 |
19 | describe('#parentReadyV', ()=>{
20 | it('should size the widget to match its parents height - padding', ()=>{
21 | expect(widget1.height).to.equal(100);
22 | widget1.vPolicy.parentReadyV();
23 | expect(widget1.height).to.equal(192);
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/sizePolicies/FixedPolicy-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('FixedPolicy', ()=>{
4 | let widget = new ST.Widgets.Button();
5 |
6 | describe('#sizeWidgetHorizontal', ()=>{
7 | it('should validate the width of the widget', ()=>{
8 | let spy = sinon.spy(widget, 'validateWidth');
9 | widget.hPolicy.sizeWidgetHorizontal();
10 | expect(spy.called).to.be.true;
11 | widget.validateWidth.restore();
12 | });
13 | });
14 |
15 | describe('#sizeWidgetVertical', ()=>{
16 | it('should validate the height of the widget', ()=>{
17 | let spy = sinon.spy(widget, 'validateHeight');
18 | widget.vPolicy.sizeWidgetVertical();
19 | expect(spy.called).to.be.true;
20 | widget.validateHeight.restore();
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/sizePolicies/SharedExpandingPolicy-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('SharedExpandingPolicy', ()=>{
4 | let widget0 = new ST.Widgets.Panel(null, {width: 400, height: 400});
5 | widget0.layout = new ST.Layouts.HBoxLayout(widget0);
6 |
7 | let widget1 = new ST.Widgets.Button(widget0);
8 | widget1.hPolicy = new ST.SizePolicies.SharedExpandingPolicy(widget1);
9 | widget1.vPolicy
10 | = new ST.SizePolicies.SharedExpandingPolicy(widget1, ST.VERTICAL);
11 |
12 | let widget2 = new ST.Widgets.Button(widget0);
13 | widget2.hPolicy = new ST.SizePolicies.SharedExpandingPolicy(widget2);
14 | widget2.vPolicy
15 | = new ST.SizePolicies.SharedExpandingPolicy(widget2, ST.VERTICAL);
16 |
17 | describe('#parentReadyH', ()=>{
18 | it('should size the widgets equal size if all have shared policy', ()=>{
19 | widget0.hPolicy.exec();
20 | expect(widget1.width).to.equal(194);
21 | expect(widget2.width).to.equal(194);
22 | });
23 |
24 | it('should handle widgets that arent of shared policy', ()=>{
25 | let nullwidget = new ST.Widgets.Panel();
26 | let widget3
27 | = new ST.Widgets.Button(widget0, {width: 100, height: 100});
28 | widget3.hPolicy = new ST.SizePolicies.FixedPolicy(widget3);
29 | widget0.hPolicy.exec();
30 | expect(widget1.width).to.equal(142);
31 | expect(widget2.width).to.equal(142);
32 | expect(widget3.width).to.equal(100);
33 | nullwidget.addChild(widget3);
34 | });
35 | });
36 |
37 | describe('#parentReadyV', ()=>{
38 | it('should size the widgets equal size if all have shared policy', ()=>{
39 | /*
40 | same as above test.
41 | */
42 | });
43 |
44 | it('should handle widgets that arent of shared policy', ()=>{
45 | /*
46 | same as above test.
47 | */
48 | });
49 | });
50 |
51 | describe('#consumeUnusedSpaceH()', ()=>{
52 | // TODO
53 | });
54 |
55 | describe('#consumeUnusedSpaceV()', ()=>{
56 | // TODO
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/spec/layoutSys/sizePolicies/index.js:
--------------------------------------------------------------------------------
1 | require('./BasePolicy-spec');
2 | require('./ExpandingPolicy-spec');
3 | require('./FixedPolicy-spec');
4 | require('./SharedExpandingPolicy-spec');
5 |
--------------------------------------------------------------------------------
/test/spec/widgets/BaseWidget-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /*
4 | TODO:
5 | test things that should happen when events are fired.
6 | eg. when padding changes _updateClipGraphic should be called.
7 | */
8 |
9 | describe('BaseWidget', ()=>{
10 | let widget0 = new ST.Widgets.Panel();
11 | let widget1 = new ST.Widgets.Panel(widget0);
12 | let widget2 = new ST.Widgets.Panel(widget1);
13 |
14 | beforeEach(()=>{
15 | widget1.layout
16 | = new ST.Layouts.FixedLayout(widget1);
17 | widget2.hPolicy
18 | = new ST.SizePolicies.FixedPolicy(widget2);
19 | widget0.hPolicy
20 | = new ST.SizePolicies.FixedPolicy(widget0);
21 | widget1.validate();
22 | widget0.validate();
23 | widget2.validate();
24 | });
25 |
26 | it('should parent one widget to another', ()=>{
27 | expect(widget2.parent).to.equal(widget1);
28 | });
29 |
30 | describe('#beginBypassUpdate()', ()=>{
31 |
32 | });
33 |
34 | describe('#endBypassUpdate()', ()=>{
35 |
36 | });
37 |
38 | describe('#validateWidth()', ()=>{
39 | it('should keep widgets width within min/max', ()=>{
40 | widget2.min.width = 10;
41 | widget2.max.width = 80;
42 | widget2.width = 100;
43 | widget2.validateWidth();
44 | expect(widget2.width).to.equal(80);
45 | widget2.width = 5;
46 | widget2.validateWidth();
47 | expect(widget2.width).to.equal(10);
48 | });
49 | });
50 |
51 | describe('#validateHeight()', ()=>{
52 | it('should keep widgets height within min/max', ()=>{
53 | widget2.min.height = 10;
54 | widget2.max.height = 80;
55 | widget2.height = 100;
56 | widget2.validateHeight();
57 | expect(widget2.height).to.equal(80);
58 | widget2.height = 5;
59 | widget2.validateHeight();
60 | expect(widget2.height).to.equal(10);
61 | });
62 | });
63 |
64 | describe('#update()', ()=>{
65 |
66 | });
67 |
68 | describe('#validate()', ()=>{
69 |
70 | });
71 |
72 | describe('#invalidate()', ()=>{
73 |
74 | });
75 |
76 | describe('#routeInvalidation()', ()=>{
77 | it('should invalidate the highest parent', ()=>{
78 | widget1.layout
79 | = new ST.Layouts.HBoxLayout(widget1);
80 | widget2.layout
81 | = new ST.Layouts.HBoxLayout(widget2);
82 | widget2.routeInvalidation();
83 | expect(widget0.valid).to.be.false;
84 | expect(widget1.valid).to.be.true;
85 | expect(widget2.valid).to.be.true;
86 | });
87 |
88 | it('should invalidate the first parent with a fixed size policy', ()=>{
89 | widget2.routeInvalidation();
90 | expect(widget0.valid).to.be.true;
91 | expect(widget1.valid).to.be.false;
92 | expect(widget2.valid).to.be.true;
93 | });
94 | });
95 |
96 | describe('#recursiveRouteUpdate()', ()=>{
97 | it('should route update to itself if no parent exist', ()=>{
98 | let updateSpy = sinon.spy(widget0, 'update');
99 | widget0.recursiveRouteUpdate();
100 | expect(updateSpy.called).to.be.true;
101 | widget0.update.restore();
102 | });
103 |
104 | it('should recursivley run this function if the parent is valid', ()=>{
105 | widget1.invalidate();
106 | let spy = sinon.spy(widget1, 'recursiveRouteUpdate');
107 | widget2.recursiveRouteUpdate();
108 | expect(spy.called).to.be.true;
109 | widget1.recursiveRouteUpdate.restore();
110 | });
111 |
112 | it('should route update to itself if parent is valid', ()=>{
113 | let updateSpy = sinon.spy(widget0, 'update');
114 | widget0.recursiveRouteUpdate();
115 | expect(updateSpy.called).to.be.true;
116 | widget0.update.restore();
117 | });
118 | });
119 |
120 | describe('#renderCanvas()', ()=>{
121 |
122 | });
123 |
124 | describe('#renderWebGL()', ()=>{
125 |
126 | });
127 |
128 | describe('#setParent()', ()=>{
129 |
130 | });
131 |
132 | describe('#addChild', ()=>{
133 | it('should set each PIXI.Containers mask to null', ()=>{
134 | let pc = new PIXI.Container();
135 | widget2.addChild(pc);
136 | expect(pc.mask).to.be.null;
137 | });
138 |
139 | it('should add its theme to each BaseWidget child added', ()=>{
140 | expect(widget2.theme).to.equal(widget1.theme);
141 | });
142 |
143 | it('should set mask to null if child has updateOnHostChanges = false '
144 | + 'for both size policies', ()=>{
145 | // should set to parent
146 | widget1.layout = new ST.Layouts.VBoxLayout(widget1);
147 | widget2.hPolicy
148 | = new ST.SizePolicies.FixedPolicy(widget2);
149 |
150 | expect(widget2.sizeProxy.mask).to.equal(widget1.clipGraphic);
151 |
152 | widget2.hPolicy
153 | = new ST.SizePolicies.ExpandingPolicy(widget2, ST.HORIZONTAL);
154 | widget2.vPolicy
155 | = new ST.SizePolicies.ExpandingPolicy(widget2, ST.VERTICAL);
156 |
157 | expect(widget2.sizeProxy.mask).to.be.null;
158 | });
159 |
160 | it('should mask children if its layout has updateOnHostChanges = true',
161 | ()=>{
162 | widget1.layout = new ST.Layouts.FixedLayout(widget1);
163 | expect(widget2.sizeProxy.mask).to.equal(widget1.clipGraphic);
164 | });
165 |
166 | // it('should add its clipGraphic to each BaseWidget child addeds'
167 | // + ' size proxy', ()=>{
168 | // expect(widget2.sizeProxy.mask).to.equal(widget1.clipGraphic);
169 | // });
170 | });
171 |
172 | describe('#addChildAt()', ()=>{
173 | // same as #addChild
174 | });
175 |
176 | describe('#onChildrenChange()', ()=>{
177 |
178 | });
179 |
180 | describe('#applyPosition()', ()=>{
181 |
182 | });
183 |
184 | describe('_updateClipGraphic()', ()=>{
185 | it('should set to size of widget - padding', ()=>{
186 | widget2.vPolicy
187 | = new ST.SizePolicies.FixedPolicy(widget2);
188 | widget2.max.width = 1000;
189 | widget2.max.height = 1000;
190 | widget2.width = 400;
191 | widget2.height = 400;
192 | widget1.update(); // should call _updateClipGraphic()
193 | expect(widget2.clipGraphic.width).to.equal(392);
194 | expect(widget2.clipGraphic.height).to.equal(392);
195 | });
196 |
197 | it('should set the pos to the top left padding values', ()=>{
198 | expect(widget2.clipGraphic.x).to.equal(4);
199 | expect(widget2.clipGraphic.y).to.equal(4);
200 | });
201 |
202 | it('should set renderable to false', ()=>{
203 | expect(widget2.clipGraphic.renderable).to.be.false;
204 | });
205 | });
206 |
207 | describe('#theme', ()=>{
208 | let aThm = new ST.Theme();
209 | it('should apply the set theme to children recursively', ()=>{
210 | widget0.theme = aThm;
211 | expect(widget1.theme).to.equal(widget0.theme);
212 | expect(widget2.theme).to.equal(widget1.theme);
213 | });
214 | });
215 |
216 | describe('#disabled', ()=>{
217 | it('should disable itself and its children when set to false', ()=>{
218 | widget0.disabled = true;
219 | expect(widget0.disabled).to.be.true;
220 | expect(widget1.disabled).to.be.true;
221 | expect(widget2.disabled).to.be.true;
222 | });
223 |
224 | it('should enable itself and its children when set to true', ()=>{
225 | widget0.disabled = false;
226 | expect(widget0.disabled).to.be.false;
227 | expect(widget1.disabled).to.be.false;
228 | expect(widget2.disabled).to.be.false;
229 | });
230 | });
231 |
232 | describe('#_evaluateMask()', ()=>{
233 | it('should mask all children if layout.updateOnHostChanges = true',
234 | ()=>{
235 | widget0.layout = new ST.Layouts.FixedLayout(widget0);
236 | expect(widget1.mask).to.equal(widget0.clipGraphic);
237 | });
238 |
239 | it('should mask child if either policy has updateOnHostChanges true'
240 | + ' and the parents layout has updateOnHostChanges false', ()=>{
241 | widget0.layout = new ST.Layouts.VBoxLayout(widget0, ST.VERTICAL);
242 | widget1.hPolicy = new ST.SizePolicies.FixedPolicy(widget1);
243 | expect(widget1.mask).to.equal(widget0.clipGraphic);
244 | });
245 |
246 | it('should set mask to null if layout has updateOnHostChanges false '
247 | + 'and childs policies have updateOnHostChanges false', ()=>{
248 | widget0.layout = new ST.Layouts.VBoxLayout(widget0, ST.VERTICAL);
249 | widget1.hPolicy
250 | = new ST.SizePolicies.ExpandingPolicy(widget1, ST.HORIZONTAL);
251 | widget1.vPolicy
252 | = new ST.SizePolicies.ExpandingPolicy(widget1, ST.VERTICAL);
253 | expect(widget1.mask).to.be.null;
254 | });
255 |
256 | it('should set mask to null if child is not a widget', ()=>{
257 | let pc = new PIXI.Container();
258 | widget0.addChild(pc);
259 | widget0._evaluateMask();
260 | expect(pc.mask).to.be.null;
261 | });
262 | });
263 |
264 | describe('#getFocusedWidget', ()=>{
265 | it('should return the widget currently in focus', ()=>{
266 | widget1.focus();
267 | expect(ST.Widgets.BaseWidget.getFocusedWidget()).to.equal(widget1);
268 | });
269 | });
270 | });
271 |
--------------------------------------------------------------------------------
/test/spec/widgets/Button-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Button', ()=>{
4 |
5 | });
6 |
--------------------------------------------------------------------------------
/test/spec/widgets/Image-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Image', ()=>{
4 | let sprite = new PIXI.Sprite();
5 | let texture = new PIXI.Texture();
6 | let image = new ST.Widgets.Image();
7 |
8 | describe('#sprite', ()=>{
9 | it('should set the sprite for the image', ()=>{
10 | image.sprite = sprite;
11 | expect(image._sprite).to.equal(sprite);
12 |
13 | // shouldnt set texture to sprite
14 | image.sprite = texture;
15 | expect(image._sprite).to.equal(sprite);
16 | });
17 | });
18 |
19 | describe('#texture', ()=>{
20 | it('should set the sprites texture', ()=>{
21 | image.texture = texture;
22 | expect(image._sprite.texture).to.equal(texture);
23 |
24 | // shouldnt set sprite for texture
25 | image.texture = sprite;
26 | expect(image._sprite.texture).to.equal(texture);
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/test/spec/widgets/Label-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Label', ()=>{
4 | let label = new ST.Widgets.Label(null, {text: 'test'});
5 |
6 | it('should set text from option if set', ()=>{
7 | expect(label.text).to.equal('test');
8 | });
9 |
10 | describe('#_textObj', ()=>{
11 | it('should have a null mask', ()=>{
12 | expect(label._textObj.mask).to.equal(null);
13 | });
14 | });
15 |
16 | describe('#_clipGraphic', ()=>{
17 | it('should be unrenderable', ()=>{
18 | expect(label._clipGraphic.renderable).to.equal(false);
19 | });
20 | });
21 |
22 | describe('#text', ()=>{
23 | it('should update _textObjs text', ()=>{
24 | label.text = 'update';
25 | expect(label._textObj.text).to.equal('update');
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/spec/widgets/Panel-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Panel', ()=>{
4 | let panel = new ST.Widgets.Panel();
5 |
6 | describe('#_bkgObj', ()=>{
7 | it('should be a sprite', ()=>{
8 | expect(panel._bkgObj).to.be.an.instanceof(PIXI.Sprite);
9 | });
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/spec/widgets/SITransform-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('SITransform', ()=>{
4 | it('should allow a widget to transform its dimensions'
5 | + ' independent from its parent', ()=>{
6 | let widget0 = new ST.Widgets.Panel();
7 | widget0.width = 1000;
8 | let widget1 = new ST.Widgets.Panel();
9 | widget1.width = 200;
10 | widget0.addChild(widget1);
11 | widget0.width = 500;
12 | expect(widget1.width).to.equal(200);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/test/spec/widgets/Slider-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // TODO: Would like to get more test here at some point
4 |
5 | describe('Slider', ()=>{
6 | let sl = new ST.Widgets.Slider(null, {width: 200, height: 30});
7 | sl.update();
8 |
9 | describe('#track', ()=>{
10 | it('should be an image widget', ()=>{
11 | expect(sl.track).to.be.an.instanceof(ST.Widgets.Image);
12 | });
13 | });
14 |
15 | describe('#trackHitRect', ()=>{
16 | it('should be a rectangle', ()=>{
17 | expect(sl.trackHitRect).to.be.an.instanceof(PIXI.Rectangle);
18 | });
19 | });
20 |
21 | describe('#button', ()=>{
22 | it('should be an image widget', ()=>{
23 | expect(sl.button).to.be.an.instanceof(ST.Widgets.Image);
24 | });
25 | });
26 |
27 | describe('#updateTrackHitRect()', ()=>{
28 | it('should set the size of the clickable area of the track', ()=>{
29 | expect(sl.trackHitRect.x).to.equal(0);
30 | expect(sl.trackHitRect.y).to.equal(-5);
31 | expect(sl.trackHitRect.width).to.equal(200);
32 | expect(sl.trackHitRect.height).to.equal(sl.track.height + 10);
33 | });
34 | });
35 |
36 | describe('#value', ()=>{
37 | it('should set the buttons pos when set', ()=>{
38 | sl.value = 0.5;
39 | const actual = 90;
40 | expect(sl.button.x).to.equal(actual);
41 | });
42 |
43 | it('should return the value from the position', ()=>{
44 | const actual = 0.5;
45 | expect(sl.value).to.equal(actual);
46 | });
47 | });
48 |
49 | describe('#orientation', ()=>{
50 | sl.orientation = ST.HORIZONTAL;
51 |
52 | it('should set the min and max heights when set', ()=>{
53 | expect(sl.min.height).to.equal(sl.button.height);
54 | expect(sl.min.width).to.equal(sl.button.width*2);
55 | expect(sl.max.height).to.equal(sl.button.height);
56 | expect(sl.max.width).to.equal(10000);
57 | });
58 |
59 | it('should set the track size', ()=>{
60 | expect(sl.track.width).to.equal(sl.width);
61 | expect(sl.track.height).to.equal(5);
62 | });
63 |
64 | it('should set alignments', ()=>{
65 | expect(sl.layout.alignment.hAlign).to.equal(ST.Alignment.left);
66 | expect(sl.layout.alignment.vAlign).to.equal(ST.Alignment.middle);
67 | });
68 |
69 | it('should set the tracks size policies', ()=>{
70 | expect(sl.track.hPolicy).to.be.an
71 | .instanceof(ST.SizePolicies.ExpandingPolicy);
72 |
73 | expect(sl.track.vPolicy).to.be.an
74 | .instanceof(ST.SizePolicies.FixedPolicy);
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/test/spec/widgets/StageWidget-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('StageWidget', ()=>{
4 | it('Should have a bounds as big as the user defined size', ()=>{
5 | let sw = new ST.Widgets.StageWidget(null, {width: 600, height: 800});
6 | let b1 = new ST.Widgets.Button(sw, {width: 900, height: 900}); //eslint-disable-line
7 | let b2 = new ST.Widgets.Panel(sw, {width: 100, height: 100, x: 700}); // eslint-disable-line
8 | let bounds = sw.getBounds();
9 |
10 | expect(bounds.width).to.equal(600);
11 | expect(bounds.height).to.equal(800);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/test/spec/widgets/TextButton-spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('TextButton', ()=>{
4 | describe('#Text', ()=>{
5 | it('should set the text for the contained label', ()=>{
6 | let tb = new ST.Widgets.TextButton();
7 | tb.text = 'meow';
8 | expect(tb.label.text).to.equal('meow');
9 | });
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/spec/widgets/index.js:
--------------------------------------------------------------------------------
1 | require('./BaseWidget-spec');
2 | require('./Button-spec');
3 | require('./Label-spec');
4 | require('./Panel-spec');
5 | require('./SITransform-spec');
6 | require('./StageWidget-spec');
7 | require('./TextButton-spec');
8 | require('./Slider-spec');
9 |
--------------------------------------------------------------------------------