From 5e7eff3aab604573f2bda05b05a36768da8cc7e0 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Thu, 4 Feb 2021 01:09:08 -0800 Subject: [PATCH] Added macro buttons --- CHANGELOG.md | 1 + src/js/color-picker.js | 76 ++++++++++++++++++ src/js/main.js | 1 + src/js/settings-macros.js | 102 +++++++++++++++++++++++++ src/js/view-control.js | 1 + src/js/view-settings.js | 1 + src/pug/index.pug | 1 + src/pug/templates/color-picker.pug | 31 ++++++++ src/pug/templates/settings-general.pug | 22 ------ src/pug/templates/settings-macros.pug | 54 +++++++++++++ src/pug/templates/view-control.pug | 7 ++ src/pug/templates/view-settings.pug | 23 +----- src/py/bbctrl/Config.py | 24 +++--- src/py/bbctrl/Mach.py | 11 +++ src/py/bbctrl/Web.py | 6 ++ src/resources/config-template.json | 14 ++++ src/static/js/jscolor.min.js | 1 + src/stylus/color-picker.styl | 9 +++ src/stylus/macros.styl | 76 ++++++++++++++++++ src/stylus/style.styl | 4 +- src/stylus/view-control.styl | 17 +++++ 21 files changed, 426 insertions(+), 56 deletions(-) create mode 100644 src/js/color-picker.js create mode 100644 src/js/settings-macros.js create mode 100644 src/pug/templates/color-picker.pug create mode 100644 src/pug/templates/settings-macros.pug create mode 100644 src/static/js/jscolor.min.js create mode 100644 src/stylus/color-picker.styl create mode 100644 src/stylus/macros.styl diff --git a/CHANGELOG.md b/CHANGELOG.md index e08ffdb..f84a947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Buildbotics CNC Controller Firmware Changelog - Move all configuration pages to ``SETTINGS``. - Moved ``Save`` button to ``SETTINGS`` pages. - Added firmware check message. + - Added macro buttons. ## v0.4.16 - Improved axis under/over warning tooltip. diff --git a/src/js/color-picker.js b/src/js/color-picker.js new file mode 100644 index 0000000..f589e89 --- /dev/null +++ b/src/js/color-picker.js @@ -0,0 +1,76 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2021, Buildbotics LLC, All rights reserved. + + This Source describes Open Hardware and is licensed under the + CERN-OHL-S v2. + + You may redistribute and modify this Source and make products + using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). + This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED + WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS + FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable + conditions. + + Source location: https://github.com/buildbotics + + As per CERN-OHL-S v2 section 4, should You produce hardware based on + these sources, You must maintain the Source Location clearly visible on + the external case of the CNC Controller or other product you make using + this Source. + + For more information, email info@buildbotics.com + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#color-picker-template', + props: { + value: { + type: String, + required: true, + twoWay: true + } + }, + + + data: function () { + return { + config: '{}' + } + }, + + + ready: function () { + window.jscolor.install(this.$el); + this.jscolor = this.$els.input.jscolor; + this.jscolor.option({ + onChange: this.change, + onInput: this.change, + showOnClick: false, + hideOnPaletteClick: true, + zIndex: 500, + palette: [ + '#e6e6e6', '#7d7d7d', '#870014', '#ec1c23', '#ff7e26', + '#fef100', '#22b14b', '#00a1e7', '#3f47cc', '#a349a4', + '#ffffff', '#c3c3c3', '#b87957', '#feaec9', '#ffc80d', + '#eee3af', '#b5e61d', '#99d9ea', '#7092be', '#c8bfe7' + ]}); + }, + + + methods: { + change: function() { + this.value = this.jscolor.toHEXString(); + this.$emit('change', this.value); + }, + + + show() {this.jscolor.show()} + } +} diff --git a/src/js/main.js b/src/js/main.js index 5a25ab4..228d1e4 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -78,6 +78,7 @@ $(function() { Vue.component('nav-menu', require('./nav-menu')); Vue.component('nav-item', require('./nav-item')); Vue.component('video', require('./video')); + Vue.component('color-picker', require('./color-picker')); require('./filters')(); diff --git a/src/js/settings-macros.js b/src/js/settings-macros.js new file mode 100644 index 0000000..0b400a5 --- /dev/null +++ b/src/js/settings-macros.js @@ -0,0 +1,102 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2021, Buildbotics LLC, All rights reserved. + + This Source describes Open Hardware and is licensed under the + CERN-OHL-S v2. + + You may redistribute and modify this Source and make products + using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). + This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED + WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS + FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable + conditions. + + Source location: https://github.com/buildbotics + + As per CERN-OHL-S v2 section 4, should You produce hardware based on + these sources, You must maintain the Source Location clearly visible on + the external case of the CNC Controller or other product you make using + this Source. + + For more information, email info@buildbotics.com + +\******************************************************************************/ + +'use strict' + +var util = require('./util'); + + +module.exports = { + template: '#settings-macros-template', + props: ['config', 'state', 'template'], + + + data: function () { + return { + dragging: -1 + } + }, + + + computed: { + macros: function () {return this.config.macros || []} + }, + + + methods: { + add: function () { + this.macros.push({name: '', path: '', color: '#e6e6e6'}) + }, + + + mousedown: function (event) {this.target = event.target}, + + + dragstart: function(event) { + if (this.target.localName == 'input') event.preventDefault(); + }, + + + drag: function(index) { + console.log('drag(' + index + ')'); + this.dragging = index; + event.preventDefault(); + }, + + + drop: function(index) { + console.log('drop(' + index + ') dragging=' + this.dragging); + + if (index == this.dragging) return; + var item = this.macros[this.dragging]; + this.macros.splice(this.dragging, 1); + this.macros.splice(index, 0, item); + this.change(); + }, + + + remove: function(index) { + this.macros.splice(index, 1); + this.change(); + }, + + + open: function(index) { + this.$root.file_dialog({ + callback: function (path) { + if (path) { + this.macros[index].path = util.display_path(path); + this.change(); + } + }.bind(this) + }) + }, + + + change: function () {this.$dispatch('input-changed')} + } +} diff --git a/src/js/view-control.js b/src/js/view-control.js index 326439d..66a51c8 100644 --- a/src/js/view-control.js +++ b/src/js/view-control.js @@ -225,6 +225,7 @@ module.exports = { methods: { send: function (msg) {this.$dispatch('send', msg)}, on_scroll: function (cm, e) {e.preventDefault()}, + run_macro: function (macro) {api.put('macro/' + macro)}, highlight_gcode: function () { diff --git a/src/js/view-settings.js b/src/js/view-settings.js index 3072793..09f284e 100644 --- a/src/js/view-settings.js +++ b/src/js/view-settings.js @@ -51,6 +51,7 @@ module.exports = { 'settings-motor': require('./settings-motor'), 'settings-tool': require('./settings-tool'), 'settings-io': require('./settings-io'), + 'settings-macros': require('./settings-macros'), 'settings-network': require('./settings-network'), 'settings-admin': require('./settings-admin') }, diff --git a/src/pug/index.pug b/src/pug/index.pug index d84702e..fee88e1 100644 --- a/src/pug/index.pug +++ b/src/pug/index.pug @@ -159,3 +159,4 @@ html(lang="en") script: include ../static/js/chart.bundle-2.9.3.min.js script: include:browserify ../js/main.js script: include ../static/js/codemirror.js + script: include ../static/js/jscolor.min.js diff --git a/src/pug/templates/color-picker.pug b/src/pug/templates/color-picker.pug new file mode 100644 index 0000000..2238687 --- /dev/null +++ b/src/pug/templates/color-picker.pug @@ -0,0 +1,31 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved. // +//- // +//- This Source describes Open Hardware and is licensed under the // +//- CERN-OHL-S v2. // +//- // +//- You may redistribute and modify this Source and make products // +//- using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). // +//- This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED // +//- WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS // +//- FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable // +//- conditions. // +//- // +//- Source location: https://github.com/buildbotics // +//- // +//- As per CERN-OHL-S v2 section 4, should You produce hardware based on // +//- these sources, You must maintain the Source Location clearly visible on // +//- the external case of the CNC Controller or other product you make using // +//- this Source. // +//- // +//- For more information, email info@buildbotics.com // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#color-picker-template(type="text/x-template") + .color-picker(@click="show", :style="{background: value}") + input(v-el:input, :value="value", @change="change", + :data-jscolor="config") diff --git a/src/pug/templates/settings-general.pug b/src/pug/templates/settings-general.pug index cbe58ce..5c80eb4 100644 --- a/src/pug/templates/settings-general.pug +++ b/src/pug/templates/settings-general.pug @@ -25,28 +25,6 @@ //- // //-///////////////////////////////////////////////////////////////////////////// -//- All rights reserved. // -//- // -//- This file ("the software") is free software: you can redistribute it // -//- and/or modify it under the terms of the GNU General Public License, // -//- version 2 as published by the Free Software Foundation. You should // -//- have received a copy of the GNU General Public License, version 2 // -//- along with the software. If not, see . // -//- // -//- The software is distributed in the hope that it will be useful, but // -//- WITHOUT ANY WARRANTY; without even the implied warranty of // -//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // -//- Lesser General Public License for more details. // -//- // -//- You should have received a copy of the GNU Lesser General Public // -//- License along with the software. If not, see // -//- . // -//- // -//- For information regarding this software email: // -//- "Joseph Coffland" // -//- // -//-///////////////////////////////////////////////////////////////////////////// - script#settings-general-template(type="text/x-template") #settings-general h1 General Configuration diff --git a/src/pug/templates/settings-macros.pug b/src/pug/templates/settings-macros.pug new file mode 100644 index 0000000..4538f50 --- /dev/null +++ b/src/pug/templates/settings-macros.pug @@ -0,0 +1,54 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved. // +//- // +//- This Source describes Open Hardware and is licensed under the // +//- CERN-OHL-S v2. // +//- // +//- You may redistribute and modify this Source and make products // +//- using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). // +//- This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED // +//- WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS // +//- FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable // +//- conditions. // +//- // +//- Source location: https://github.com/buildbotics // +//- // +//- As per CERN-OHL-S v2 section 4, should You produce hardware based on // +//- these sources, You must maintain the Source Location clearly visible on // +//- the external case of the CNC Controller or other product you make using // +//- this Source. // +//- // +//- For more information, email info@buildbotics.com // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#settings-macros-template(type="text/x-template") + #settings-macros + h1 Macro Configuration + + .toolbar + button.pure-button(title="Add a new macro.", @click="add") + .fa.fa-plus + | Add + + ol.macros + li(v-for="macro in macros") + label.index {{"#" + ($index + 1)}} + .macro(draggable="true", @dragstart="dragstart", @mousedown="mousedown", + @drag="drag($index)", @dragover.prevent="", + @drop.prevent="drop($index)") + .grip + color-picker(:value.sync="macro.color", @change="change", + title="Set the macro button color.") + input.name(@change="change", v-model="macro.name", + title="Set the macro button name.") + input.path(@click="open($index)", :value="macro.path", readonly, + title="Select the macro file") + .actions + .fa.fa-folder-open(@click="open($index)", + title="Select the macro file.") + .fa.fa-trash(@click="remove($index)", + :title="'Delete macro ' + ($index + 1) + '.'") diff --git a/src/pug/templates/view-control.pug b/src/pug/templates/view-control.pug index f54c196..2c61ff4 100644 --- a/src/pug/templates/view-control.pug +++ b/src/pug/templates/view-control.pug @@ -111,6 +111,13 @@ script#view-control-template(type="text/x-template") title=`Home {{'${axis}' | upper}} axis.`, @click=`set_home('${axis}', axis_position)`) Set + .macros + button.pure-button.macro(v-for="macro in config.macros", + v-if="macro.path", @click="run_macro($index + 1)", + :title="'Run macro ' + ($index + 1) + ' ' + macro.name + '\n' + macro.path", + :style="{background: macro.color}") + | {{($index + 1) + ' ' + macro.name}} + table.info tr th State diff --git a/src/pug/templates/view-settings.pug b/src/pug/templates/view-settings.pug index 98cdd53..e67724d 100644 --- a/src/pug/templates/view-settings.pug +++ b/src/pug/templates/view-settings.pug @@ -25,28 +25,6 @@ //- // //-///////////////////////////////////////////////////////////////////////////// -//- All rights reserved. // -//- // -//- This file ("the software") is free software: you can redistribute it // -//- and/or modify it under the terms of the GNU General Public License, // -//- version 2 as published by the Free Software Foundation. You should // -//- have received a copy of the GNU General Public License, version 2 // -//- along with the software. If not, see . // -//- // -//- The software is distributed in the hope that it will be useful, but // -//- WITHOUT ANY WARRANTY; without even the implied warranty of // -//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // -//- Lesser General Public License for more details. // -//- // -//- You should have received a copy of the GNU Lesser General Public // -//- License along with the software. If not, see // -//- . // -//- // -//- For information regarding this software email: // -//- "Joseph Coffland" // -//- // -//-///////////////////////////////////////////////////////////////////////////// - script#view-settings-template(type="text/x-template") div nav.navbar @@ -62,6 +40,7 @@ script#view-settings-template(type="text/x-template") a.nav-item(href="#settings:tool") Tool a.nav-item(href="#settings:io") I/O + a.nav-item(href="#settings:macros") Macros a.nav-item(href="#settings:network") Network a.nav-item(href="#settings:admin") Admin diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index 35a447f..1c8ee21 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -113,13 +113,13 @@ class Config(object): config[name] = template['min'] if template['type'] == 'list': - config = config[name] + if 'index' in template: + config = config[name] + for i in range(len(template['index'])): + if len(config) <= i: config.append({}) - for i in range(len(template['index'])): - if len(config) <= i: config.append({}) - - for name, tmpl in template['template'].items(): - self.__defaults(config[i], name, tmpl) + for name, tmpl in template['template'].items(): + self.__defaults(config[i], name, tmpl) else: for name, tmpl in template.items(): @@ -208,11 +208,13 @@ class Config(object): # Handle list if tmpl['type'] == 'list': - for i in range(len(tmpl['index'])): - if config is not None and i < len(config): conf = config[i] - else: conf = None - self._encode(name, index + tmpl['index'][i], conf, - tmpl['template'], with_defaults) + if 'index' in tmpl: + for i in range(len(tmpl['index'])): + if config is not None and i < len(config): conf = config[i] + else: conf = None + self._encode(name, index + tmpl['index'][i], conf, + tmpl['template'], with_defaults) + else: self.values[name] = value return # Update config values diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py index 081473f..a37be92 100644 --- a/src/py/bbctrl/Mach.py +++ b/src/py/bbctrl/Mach.py @@ -353,3 +353,14 @@ class Mach(Comm): def modbus_write(self, addr, value): self._i2c_block(Cmd.modbus_write(addr, value)) + + + def macro(self, macro): + macros = self.ctrl.config.get('macros') + if len(macros) < macro: raise Exception('Invalid macro id %d' % macro) + path = 'Home/' + macros[macro - 1]['path'] + + self.mlog.info('Running macro %d %s' % (macro, path)) + self._begin_cycle('running') + self.planner.load(path) + super().resume() diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index 8de58b3..508b900 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -329,6 +329,11 @@ class USBEjectHandler(bbctrl.APIHandler): subprocess.Popen(['/usr/local/bin/eject-usb', '/media/' + path]) +class MacroHandler(bbctrl.APIHandler): + def put_ok(self, macro): + self.get_ctrl().mach.macro(int(macro)) + + class PathHandler(bbctrl.APIHandler): @gen.coroutine def get(self, dataType, path, *args): @@ -580,6 +585,7 @@ class Web(tornado.web.Application): (r'/api/usb/update', USBUpdateHandler), (r'/api/usb/eject/(.*)', USBEjectHandler), (r'/api/fs/(.*)', bbctrl.FileSystemHandler), + (r'/api/macro/(\d+)', MacroHandler), (r'/api/(path)/(.*)', PathHandler), (r'/api/(positions)/(.*)', PathHandler), (r'/api/(speeds)/(.*)', PathHandler), diff --git a/src/resources/config-template.json b/src/resources/config-template.json index 4a72176..869d88b 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -483,5 +483,19 @@ "type": "bool", "default": true } + }, + + "macros": { + "type": "list", + "default": [ + {"name": "test1", "color": "#eceff1", "path": "macros/macro1.nc"}, + {"name": "test2", "color": "#8d6e63", "path": "macros/macro2.nc"}, + {"name": "test3", "color": "#ff7043", "path": "macros/macro3.nc"} + ], + "template": { + "name": {"type": "string"}, + "path": {"type": "string"}, + "color": {"type": "string", "default": "#e6e6e6"} + } } } diff --git a/src/static/js/jscolor.min.js b/src/static/js/jscolor.min.js new file mode 100644 index 0000000..0677a02 --- /dev/null +++ b/src/static/js/jscolor.min.js @@ -0,0 +1 @@ +(function(global,factory){"use strict";if(typeof module==="object"&&typeof module.exports==="object"){module.exports=global.document?factory(global):function(win){if(!win.document){throw new Error("jscolor needs a window with document")}return factory(win)};return}factory(global)})(typeof window!=="undefined"?window:this,function(window){"use strict";var jscolor=function(){var jsc={initialized:false,instances:[],readyQueue:[],register:function(){if(typeof window!=="undefined"&&window.document){window.document.addEventListener("DOMContentLoaded",jsc.pub.init,false)}},installBySelector:function(selector,rootNode){rootNode=rootNode?jsc.node(rootNode):window.document;if(!rootNode){throw new Error("Missing root node")}var elms=rootNode.querySelectorAll(selector);var matchClass=new RegExp("(^|\\s)("+jsc.pub.lookupClass+")(\\s*(\\{[^}]*\\})|\\s|$)","i");for(var i=0;i-1},isButtonEmpty:function(el){switch(jsc.nodeName(el)){case"input":return!el.value||el.value.trim()==="";case"button":return el.textContent.trim()===""}return null},isPassiveEventSupported:function(){var supported=false;try{var opts=Object.defineProperty({},"passive",{get:function(){supported=true}});window.addEventListener("testPassive",null,opts);window.removeEventListener("testPassive",null,opts)}catch(e){}return supported}(),isColorAttrSupported:function(){var elm=window.document.createElement("input");if(elm.setAttribute){elm.setAttribute("type","color");if(elm.type.toLowerCase()=="color"){return true}}return false}(),dataProp:"_data_jscolor",setData:function(){var obj=arguments[0];if(arguments.length===3){var data=obj.hasOwnProperty(jsc.dataProp)?obj[jsc.dataProp]:obj[jsc.dataProp]={};var prop=arguments[1];var value=arguments[2];data[prop]=value;return true}else if(arguments.length===2&&typeof arguments[1]==="object"){var data=obj.hasOwnProperty(jsc.dataProp)?obj[jsc.dataProp]:obj[jsc.dataProp]={};var map=arguments[1];for(var prop in map){if(map.hasOwnProperty(prop)){data[prop]=map[prop]}}return true}throw new Error("Invalid arguments")},removeData:function(){var obj=arguments[0];if(!obj.hasOwnProperty(jsc.dataProp)){return true}for(var i=1;i=3&&(mR=par[0].match(re))&&(mG=par[1].match(re))&&(mB=par[2].match(re))){ret.format="rgb";ret.rgba=[parseFloat(mR[1])||0,parseFloat(mG[1])||0,parseFloat(mB[1])||0,null];if(par.length>=4&&(mA=par[3].match(re))){ret.format="rgba";ret.rgba[3]=parseFloat(mA[1])||0}return ret}}return false},parsePaletteValue:function(mixed){var vals=[];if(typeof mixed==="string"){mixed.replace(/#[0-9A-F]{3}([0-9A-F]{3})?|rgba?\(([^)]*)\)/gi,function(val){vals.push(val)})}else if(Array.isArray(mixed)){vals=mixed}var colors=[];for(var i=0;ivs[a]?-vp[a]+tp[a]+ts[a]/2>vs[a]/2&&tp[a]+ts[a]-ps[a]>=0?tp[a]+ts[a]-ps[a]:tp[a]:tp[a],-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c>vs[b]?-vp[b]+tp[b]+ts[b]/2>vs[b]/2&&tp[b]+ts[b]-l-l*c>=0?tp[b]+ts[b]-l-l*c:tp[b]+ts[b]-l+l*c:tp[b]+ts[b]-l+l*c>=0?tp[b]+ts[b]-l+l*c:tp[b]+ts[b]-l-l*c]}var x=pp[a];var y=pp[b];var positionValue=thisObj.fixed?"fixed":"absolute";var contractShadow=(pp[0]+ps[0]>tp[0]||pp[0]0?Math.ceil(sampleCount/cols):0;cellW=Math.max(1,Math.floor((width-(cols-1)*thisObj.paletteSpacing)/cols));cellH=thisObj.paletteHeight?Math.min(thisObj.paletteHeight,cellW):cellW}if(rows){height=rows*cellH+(rows-1)*thisObj.paletteSpacing}return{cols:cols,rows:rows,cellW:cellW,cellH:cellH,width:width,height:height}},getControlPadding:function(thisObj){return Math.max(thisObj.padding/2,2*thisObj.pointerBorderWidth+thisObj.pointerThickness-thisObj.controlBorderWidth)},getPadYChannel:function(thisObj){switch(thisObj.mode.charAt(1).toLowerCase()){case"v":return"v";break}return"s"},getSliderChannel:function(thisObj){if(thisObj.mode.length>2){switch(thisObj.mode.charAt(2).toLowerCase()){case"s":return"s";break;case"v":return"v";break}}return null},triggerCallback:function(thisObj,prop){if(!thisObj[prop]){return}var callback=null;if(typeof thisObj[prop]==="string"){try{callback=new Function(thisObj[prop])}catch(e){console.error(e)}}else{callback=thisObj[prop]}if(callback){callback.call(thisObj)}},triggerGlobal:function(eventNames){var inst=jsc.getInstances();for(var i=0;i0){for(var y=0;y=2&&typeof arguments[0]==="string"){try{if(!setOption(arguments[0],arguments[1])){return false}}catch(e){console.warn(e);return false}this.redraw();this.exposeColor();return true}else if(arguments.length===1&&typeof arguments[0]==="object"){var opts=arguments[0];var success=true;for(var opt in opts){if(opts.hasOwnProperty(opt)){try{if(!setOption(opt,opts[opt])){success=false}}catch(e){console.warn(e);success=false}}}this.redraw();this.exposeColor();return success}throw new Error("Invalid arguments")};this.channel=function(name,value){if(typeof name!=="string"){throw new Error("Invalid value for channel name: "+name)}if(value===undefined){if(!this.channels.hasOwnProperty(name.toLowerCase())){console.warn("Getting unknown channel: "+name);return false}return this.channels[name.toLowerCase()]}else{var res=false;switch(name.toLowerCase()){case"r":res=this.fromRGBA(value,null,null,null);break;case"g":res=this.fromRGBA(null,value,null,null);break;case"b":res=this.fromRGBA(null,null,value,null);break;case"h":res=this.fromHSVA(value,null,null,null);break;case"s":res=this.fromHSVA(null,value,null,null);break;case"v":res=this.fromHSVA(null,null,value,null);break;case"a":res=this.fromHSVA(null,null,null,value);break;default:console.warn("Setting unknown channel: "+name);return false}if(res){this.redraw();return true}}return false};this.trigger=function(eventNames){var evs=jsc.strList(eventNames);for(var i=0;i255/2};this.hide=function(){if(isPickerOwner()){detachPicker()}};this.show=function(){drawPicker()};this.redraw=function(){if(isPickerOwner()){drawPicker()}};this.getFormat=function(){return this._currentFormat};this._setFormat=function(format){this._currentFormat=format.toLowerCase()};this.hasAlphaChannel=function(){if(this.alphaChannel==="auto"){return this.format.toLowerCase()==="any"||jsc.isAlphaFormat(this.getFormat())||this.alpha!==undefined||this.alphaElement!==undefined}return this.alphaChannel};this.processValueInput=function(str){if(!this.fromString(str)){this.exposeColor()}};this.processAlphaInput=function(str){if(!this.fromHSVA(null,null,null,parseFloat(str))){this.exposeColor()}};this.exposeColor=function(flags){var colorStr=this.toString();var fmt=this.getFormat();jsc.setDataAttr(this.targetElement,"current-color",colorStr);if(!(flags&jsc.flags.leaveValue)&&this.valueElement){if(fmt==="hex"||fmt==="hexa"){if(!this.uppercase){colorStr=colorStr.toLowerCase()}if(!this.hash){colorStr=colorStr.replace(/^#/,"")}}this.setValueElementValue(colorStr)}if(!(flags&jsc.flags.leaveAlpha)&&this.alphaElement){var alphaVal=Math.round(this.channels.a*100)/100;this.setAlphaElementValue(alphaVal)}if(!(flags&jsc.flags.leavePreview)&&this.previewElement){var previewPos=null;if(jsc.isTextInput(this.previewElement)||jsc.isButton(this.previewElement)&&!jsc.isButtonEmpty(this.previewElement)){previewPos=this.previewPosition}this.setPreviewElementBg(this.toRGBAString())}if(isPickerOwner()){redrawPad();redrawSld();redrawASld()}};this.setPreviewElementBg=function(color){if(!this.previewElement){return}var position=null;var width=null;if(jsc.isTextInput(this.previewElement)||jsc.isButton(this.previewElement)&&!jsc.isButtonEmpty(this.previewElement)){position=this.previewPosition;width=this.previewSize}var backgrounds=[];if(!color){backgrounds.push({image:"none",position:"left top",size:"auto",repeat:"no-repeat",origin:"padding-box"})}else{backgrounds.push({image:jsc.genColorPreviewGradient(color,position,width?width-jsc.pub.previewSeparator.length:null),position:"left top",size:"auto",repeat:position?"repeat-y":"repeat",origin:"padding-box"});var preview=jsc.genColorPreviewCanvas("rgba(0,0,0,0)",position?{left:"right",right:"left"}[position]:null,width,true);backgrounds.push({image:"url('"+preview.canvas.toDataURL()+"')",position:(position||"left")+" top",size:preview.width+"px "+preview.height+"px",repeat:position?"repeat-y":"repeat",origin:"padding-box"})}var bg={image:[],position:[],size:[],repeat:[],origin:[]};for(var i=0;i=0;i-=1){var pres=presetsArr[i];if(!pres){continue}if(!jsc.pub.presets.hasOwnProperty(pres)){console.warn("Unknown preset: %s",pres);continue}for(var opt in jsc.pub.presets[pres]){if(jsc.pub.presets[pres].hasOwnProperty(opt)){try{setOption(opt,jsc.pub.presets[pres][opt])}catch(e){console.warn(e)}}}}var nonProperties=["preset"];for(var opt in opts){if(opts.hasOwnProperty(opt)){if(nonProperties.indexOf(opt)===-1){try{setOption(opt,opts[opt])}catch(e){console.warn(e)}}}}if(this.container===undefined){this.container=window.document.body}else{this.container=jsc.node(this.container)}if(!this.container){throw new Error("Cannot instantiate color picker without a container element")}this.targetElement=jsc.node(targetElement);if(!this.targetElement){if(typeof targetElement==="string"&&/^[a-zA-Z][\w:.-]*$/.test(targetElement)){var possiblyId=targetElement;throw new Error("If '"+possiblyId+"' is supposed to be an ID, please use '#"+possiblyId+"' or any valid CSS selector.")}throw new Error("Cannot instantiate color picker without a target element")}if(this.targetElement.jscolor&&this.targetElement.jscolor instanceof jsc.pub){throw new Error("Color picker already installed on this element")}this.targetElement.jscolor=this;jsc.addClass(this.targetElement,jsc.pub.className);jsc.instances.push(this);if(jsc.isButton(this.targetElement)){if(this.targetElement.type.toLowerCase()!=="button"){this.targetElement.type="button"}if(jsc.isButtonEmpty(this.targetElement)){jsc.removeChildren(this.targetElement);this.targetElement.appendChild(window.document.createTextNode(" "));var compStyle=jsc.getCompStyle(this.targetElement);var currMinWidth=parseFloat(compStyle["min-width"])||0;if(currMinWidth-1){var color=jsc.parseColorString(initValue);this._currentFormat=color?color.format:"hex"}else{this._currentFormat=this.format.toLowerCase()}this.processValueInput(initValue);if(initAlpha!==undefined){this.processAlphaInput(initAlpha)}}};jsc.pub.className="jscolor";jsc.pub.activeClassName="jscolor-active";jsc.pub.looseJSON=true;jsc.pub.presets={};jsc.pub.presets["default"]={};jsc.pub.presets["light"]={backgroundColor:"rgba(255,255,255,1)",controlBorderColor:"rgba(187,187,187,1)",buttonColor:"rgba(0,0,0,1)"};jsc.pub.presets["dark"]={backgroundColor:"rgba(51,51,51,1)",controlBorderColor:"rgba(153,153,153,1)",buttonColor:"rgba(240,240,240,1)"};jsc.pub.presets["small"]={width:101,height:101,padding:10,sliderSize:14,paletteCols:8};jsc.pub.presets["medium"]={width:181,height:101,padding:12,sliderSize:16,paletteCols:10};jsc.pub.presets["large"]={width:271,height:151,padding:12,sliderSize:24,paletteCols:15};jsc.pub.presets["thin"]={borderWidth:1,controlBorderWidth:1,pointerBorderWidth:1};jsc.pub.presets["thick"]={borderWidth:2,controlBorderWidth:2,pointerBorderWidth:2};jsc.pub.sliderInnerSpace=3;jsc.pub.chessboardSize=8;jsc.pub.chessboardColor1="#666666";jsc.pub.chessboardColor2="#999999";jsc.pub.previewSeparator=["rgba(255,255,255,.65)","rgba(128,128,128,.65)"];jsc.pub.init=function(){if(jsc.initialized){return}window.document.addEventListener("mousedown",jsc.onDocumentMouseDown,false);window.document.addEventListener("keyup",jsc.onDocumentKeyUp,false);window.addEventListener("resize",jsc.onWindowResize,false);window.addEventListener("scroll",jsc.onWindowScroll,false);jsc.pub.install();jsc.initialized=true;while(jsc.readyQueue.length){var func=jsc.readyQueue.shift();func()}};jsc.pub.install=function(rootNode){var success=true;try{jsc.installBySelector("[data-jscolor]",rootNode)}catch(e){success=false;console.warn(e)}if(jsc.pub.lookupClass){try{jsc.installBySelector("input."+jsc.pub.lookupClass+", "+"button."+jsc.pub.lookupClass,rootNode)}catch(e){}}return success};jsc.pub.ready=function(func){if(typeof func!=="function"){console.warn("Passed value is not a function");return false}if(jsc.initialized){func()}else{jsc.readyQueue.push(func)}return true};jsc.pub.trigger=function(eventNames){var triggerNow=function(){jsc.triggerGlobal(eventNames)};if(jsc.initialized){triggerNow()}else{jsc.pub.ready(triggerNow)}};jsc.pub.hide=function(){if(jsc.picker&&jsc.picker.owner){jsc.picker.owner.hide()}};jsc.pub.chessboard=function(color){if(!color){color="rgba(0,0,0,0)"}var preview=jsc.genColorPreviewCanvas(color);return preview.canvas.toDataURL()};jsc.pub.background=function(color){var backgrounds=[];backgrounds.push(jsc.genColorPreviewGradient(color));var preview=jsc.genColorPreviewCanvas();backgrounds.push(["url('"+preview.canvas.toDataURL()+"')","left top","repeat"].join(" "));return backgrounds.join(", ")};jsc.pub.options={};jsc.pub.lookupClass="jscolor";jsc.pub.installByClassName=function(){console.error('jscolor.installByClassName() is DEPRECATED. Use data-jscolor="" attribute instead of a class name.'+jsc.docsRef);return false};jsc.register();return jsc.pub}();if(typeof window.jscolor==="undefined"){window.jscolor=window.JSColor=jscolor}return jscolor}); diff --git a/src/stylus/color-picker.styl b/src/stylus/color-picker.styl new file mode 100644 index 0000000..916c061 --- /dev/null +++ b/src/stylus/color-picker.styl @@ -0,0 +1,9 @@ +.color-picker + height 1.5em + width 1.5em + border 1px solid #aaa + cursor pointer + + input + opacity 0 + cursor pointer diff --git a/src/stylus/macros.styl b/src/stylus/macros.styl new file mode 100644 index 0000000..4022d18 --- /dev/null +++ b/src/stylus/macros.styl @@ -0,0 +1,76 @@ +#settings-macros + .toolbar + text-align right + + .fa + margin-right 0.25em + + .macros + border 1px inset #ccc + padding 0.5em + margin 0.5em 0 + + li + display flex + align-items center + + .index + padding 4px + width 2.5em + + label + font-weight bold + + .macro + display flex + flex 1 + align-items center + background #eee + margin 2px 0 + white-space nowrap + user-select none + + > * + margin 2px + + input + border 0 + line-height 1.75em + padding 0 4px + user-select normal + + .name + width 5em + + .path + flex 1 + + .actions + display inline-block + + .fa + margin 0 4px + cursor pointer + + &:hover + opacity 0.8 + + .grip + content '....' + width 10px + height 24px + display inline-block + overflow hidden + line-height 5px + padding 3px 4px + cursor move + vertical-align middle + font-size 12px + font-family sans-serif + letter-spacing 2px + color #cccccc + text-shadow 1px 0 1px black + white-space normal + + .grip:after + content '.. .. .. ..' diff --git a/src/stylus/style.styl b/src/stylus/style.styl index 7faf192..f7f1271 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -2,7 +2,7 @@ This file is part of the Buildbotics firmware. - Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved. + Copyright (c) 2015 - 2021, Buildbotics LLC, All rights reserved. This Source describes Open Hardware and is licensed under the CERN-OHL-S v2. @@ -47,6 +47,7 @@ tt display none @import('status-colors') +@import('color-picker') @import('attention') @import('status') @import('header') @@ -80,3 +81,4 @@ tt @import('error-message') @import('wifi') @import('cheat-sheet') +@import('macros') diff --git a/src/stylus/view-control.styl b/src/stylus/view-control.styl index faf6602..750d6a7 100644 --- a/src/stylus/view-control.styl +++ b/src/stylus/view-control.styl @@ -118,6 +118,23 @@ font-size 10pt text-anchor middle + .macros + display grid + grid-template-columns repeat(8, 11.625%) + column-gap 1% + row-gap 8px + margin-bottom 10px + + .macro + text-align left + overflow hidden + text-overflow ellipsis + white-space nowrap + padding 0.5em + + &:hover + opacity 0.9 + .info empty-cells show width 32.9% -- 2.27.0