Skip to content

Commit

Permalink
feat: Let macros define display options (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
jschuh committed Jun 1, 2023
1 parent 8481408 commit 55a8501
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 63 deletions.
171 changes: 121 additions & 50 deletions src/components/inputs/MacroButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:color="color"
:class="paramArray.length ? 'macroWithParameters' : ''"
:loading="loadings.includes('macro_' + macro.name)"
:disabled="disabled"
:disabled="disabled || paramsRequired"
@click="doSendMacro(macro.name)">
{{ alias ? alias : macro.name.replace(/_/g, ' ') }}
</v-btn>
Expand All @@ -23,15 +23,35 @@
</v-btn>
</template>
<v-card :max-width="paramsOverlayWidth">
<v-card-text class="py-2">
<v-card-text class="py-2"><v-form ref="form">
<v-row class="my-2">
<v-col v-for="(name, key) in paramArray" :key="'param_' + key" :cols="paramCssCols">
<v-text-field
v-model="params[name].value"
:label="name"
:placeholder="params[name].default"
<v-col v-for="(param, key) in paramArray" :key="'param_' + key" :cols="paramCssCols">
<v-select v-if="param.type == 'select'"
v-model="param.value"
:label="param.name"
:items="param.hints?.options"
hide-details
outlined
clearable
:clear-icon="mdiRefresh"
dense></v-select>
<v-checkbox v-else-if="param.type == 'checkbox'"
v-model="param.value"
:label="param.name"
:true-value="(param.hints?.options ?? [])[0] ?? 1"
:false-value="(param.hints?.options ?? [])[1] ?? 0"
hide-details
outlined
dense></v-checkbox>
<v-text-field v-else
v-model="param.value"
:label="param.name"
:rules="param.rules"
:type="param.fieldType"
:placeholder="param.default ?? ''"
:persistent-placeholder="true"
hide-details
hide-spin-buttons
outlined
dense
clearable
Expand All @@ -46,7 +66,7 @@
</v-btn>
</v-col>
</v-row>
</v-card-text>
</v-form></v-card-text>
</v-card>
</v-menu>
<template v-else>
Expand All @@ -59,20 +79,40 @@
<v-icon>{{ mdiMenuDown }}</v-icon>
</v-btn>
<v-dialog v-model="paramsDialog">
<panel :title="macro.name" :card-class="`macro-params-mobile-${macro.name}`" :margin-bottom="0">
<panel :title="macro.name" :card-class="`macro-params-mobile-${macro.name}`" :margin-bottom="0"><v-form ref="form">
<template #buttons>
<v-btn icon tile @click="paramsDialog = false">
<v-icon>{{ mdiCloseThick }}</v-icon>
</v-btn>
</template>
<v-card-text>
<v-row>
<v-col v-for="(name, key) in paramArray" :key="'param_mobile_' + key" :cols="6">
<v-text-field
v-model="params[name].value"
:label="name"
:placeholder="params[name].default"
<v-col v-for="(param, key) in paramArray" :key="'param_mobile_' + key" :cols="6">
<v-select v-if="value.type == 'select'"
v-model="param.value"
:label="param.name"
:items="param.hints?.options"
hide-details
outlined
clearable
:clear-icon="mdiRefresh"
dense></v-select>
<v-checkbox v-else-if="param.type == 'checkbox'"
v-model="param.value"
:label="param.name"
:true-value="param.hints?.options[0] ?? 1"
:false-value="param.hints?.options[1] ?? 0"
hide-details
outlined
dense></v-checkbox>
<v-text-field v-else
v-model="param.value"
:label="param.name"
:rules="param.rules"
:type="param.fieldType"
:placeholder="param.default ?? ''"
:persistent-placeholder="true"
hide-spin-buttons
hide-details
outlined
dense
Expand All @@ -87,7 +127,7 @@
{{ $t('Panels.MacrosPanel.Send') }}
</v-btn>
</v-card-actions>
</panel>
</v-form></panel>
</v-dialog>
</template>
</template>
Expand All @@ -96,20 +136,18 @@
<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop, Watch } from 'vue-property-decorator'
import { Mixins, Prop, Ref, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { GuiMacrosStateMacrogroupMacro } from '@/store/gui/macros/types'
import { mdiCloseThick, mdiMenuDown, mdiRefresh } from '@mdi/js'
import Panel from '@/components/ui/Panel.vue'
import { PrinterStateMacroParam } from '@/store/printer/types'
interface param {
type: 'int' | 'double' | 'string' | null
default: string | number | null
value: string | number | null
}
interface params {
[key: string]: param
interface NamedUiParam extends PrinterStateMacroParam {
value?: string | number | null
name: string
fieldType?: string | null
rules: any[] | undefined
}
@Component({
Expand All @@ -123,10 +161,55 @@ export default class MacroButton extends Mixins(BaseMixin) {
mdiMenuDown = mdiMenuDown
mdiRefresh = mdiRefresh
private paramArray: string[] = []
private params: params = {}
private params: { [ key : string ] : PrinterStateMacroParam } = {}
private paramsDialog = false
get paramArray() : NamedUiParam[] {
// Sort first with parameters that need values, then alphabetically.
let params = this.params
let paramList : string[] = Object.keys(params)
paramList.sort()
function makeRules(param : PrinterStateMacroParam) : any[] {
let check_range = function(value:string, option:string, compare:Function) : boolean {
let test = (param.hints ?? {})[option]
if (test == null || !value)
return true
if (param.type == 'int') {
return compare(value - test)
}
if (param.type == 'float')
return compare(parseFloat(value) - test)
return compare(value.length - test)
}
return [(v:string) => !v || param.type != 'float' || !Number.isNaN(parseFloat(v)),
(v:string) => !v || param.type != 'int' || Number.isInteger(parseFloat(v)),
(v:string) => check_range(v, 'max', (d) => (d <= 0)),
(v:string) => check_range(v, 'min', (d) => (d >= 0)),
(v:string) => check_range(v, 'below', (d) => (d < 0)),
(v:string) => check_range(v, 'above', (d) => (d > 0)),
(v:string) => !!v || !!param?.default || !param?.hints?.required]
}
let hasHints : boolean = this.klipperMacro.variables?.front_end_hints
return paramList.map((name) => ({...{
name: name,
fieldType: ['int','float'].includes(params[name].type) ? 'number' : null,
rules: hasHints ? makeRules(params[name]) : undefined,
value: ['select','checkbox'].includes(params[name].type ?? '') ? String(params[name].default ?? '') : null
},
...(params[name])}));
}
get paramsRequired() : boolean {
for (const [_, param] of Object.entries(this.params)) {
if (param?.hints?.required && !param?.default)
return true
}
return false
}
@Prop({ required: true })
declare readonly macro: GuiMacrosStateMacrogroupMacro
Expand All @@ -139,6 +222,9 @@ export default class MacroButton extends Mixins(BaseMixin) {
@Prop({ default: false })
declare readonly disabled: boolean
@Ref('form')
declare readonly form
get klipperMacro() {
return this.$store.getters['printer/getMacro'](this.macro.name)
}
Expand All @@ -165,27 +251,13 @@ export default class MacroButton extends Mixins(BaseMixin) {
return 200 * this.paramCols
}
@Watch('klipperMacro')
@Watch('klipperMacro', {deep:true, immediate: true})
klipperMacroChange() {
this.refreshParams()
}
refreshParams() {
this.paramArray.splice(0, this.paramArray.length)
this.params = {}
if (this.klipperMacro?.params !== null) {
Object.keys(this.klipperMacro.params).forEach((name: string) => {
if (!name.startsWith('_')) {
this.paramArray.push(name)
this.params[name] = {
type: this.klipperMacro.params[name].type,
default: this.klipperMacro.params[name].default,
value: '',
}
}
})
}
this.$set(this, 'params', this.klipperMacro?.params ?? {})
}
doSendMacro(gcode: string) {
Expand All @@ -197,11 +269,14 @@ export default class MacroButton extends Mixins(BaseMixin) {
}
sendWithParams() {
if (!this.form.validate())
return
let params: string[] = []
this.paramArray.forEach((paramname: string) => {
if (this.params[paramname].value !== null && this.params[paramname].value !== '') {
let tmp: string = paramname
tmp += this.isGcodeStyle ? this.params[paramname].value : `=${this.params[paramname].value}`
this.paramArray.forEach((param) => {
if (param.value !== null && param.value !== '' &&
(!['select','checkbox'].includes(param.type) || param.value !== param.default)) {
let tmp: string = param.name
tmp += this.isGcodeStyle ? param.value : `=${param.value}`
params.push(tmp)
}
Expand All @@ -210,10 +285,6 @@ export default class MacroButton extends Mixins(BaseMixin) {
const gcode = this.macro.name + ' ' + params.join(' ')
this.doSendMacro(gcode)
}
mounted() {
this.refreshParams()
}
}
</script>
Expand Down
6 changes: 5 additions & 1 deletion src/components/panels/MacrosPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export default class MacrosPanel extends Mixins(BaseMixin) {
get macros() {
const macros = this.$store.getters['printer/getMacros']
return macros.filter((macro: PrinterStateMacro) => !this.hiddenMacros.includes(macro.name.toLowerCase()))
return macros.filter((macro: PrinterStateMacro) => !macro.hidden && !this.hiddenMacros.includes(macro.name.toLowerCase()))
}
get macroState() {
return this.$store.getters['printer/getMacro'](this.macro.name)
}
}
</script>
27 changes: 23 additions & 4 deletions src/plugins/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ export function formatTime(date: Date): string {
return hours + ':' + minutes + ':' + seconds
}

export function getMacroParams(macro: { gcode: string }): PrinterStateMacroParams {
export function getMacroParamsFromConfig(macro: { gcode: string }): PrinterStateMacroParams {
const paramRegex =
/{%?.*?params\.([A-Za-z_0-9]+)(?:\|(int|string|double))?(?:\|default\('?"?(.*?)"?'?\))?(?:\|(int|string))?.*?%?}/
/{%?.*?params\.([A-Za-z_0-9]+)(?:\|(int|string|float))?(?:\|default\('?"?(.*?)"?'?\))?(?:\|(int|string))?.*?%?}/

let params = paramRegex.exec(macro.gcode)
let currentMatch = macro.gcode
Expand All @@ -201,10 +201,10 @@ export function getMacroParams(macro: { gcode: string }): PrinterStateMacroParam
ret = {}
}
const name = params[1]
const t: 'int' | 'string' | 'double' | null = (params[2] ?? params[4] ?? null) as
const t: 'int' | 'string' | 'float' | null = (params[2] ?? params[4] ?? null) as
| 'int'
| 'string'
| 'double'
| 'float'
| null
const def = params[3] ?? null
ret[`${name}`] = {
Expand Down Expand Up @@ -237,6 +237,25 @@ export function getMacroParams(macro: { gcode: string }): PrinterStateMacroParam
return ret
}

export function getMacroParamsFromState(hints: any)
: PrinterStateMacroParams {
if (!hints.params)
return null
const allowedTypes = ['int', 'float', 'string', 'select', 'checkbox']
let newParams : PrinterStateMacroParams = {}
Object.keys(hints.params).forEach((name: string) => {
newParams![name.toUpperCase()] = {
// The type list here is from PrinterStateMacroParam
type: !Array.isArray(hints.params[name].type) ? hints.params[name].type :
hints.params[name].type.find((type:string) => allowedTypes.includes(type)),
default: hints.params[name].default ? String(hints.params[name].default) : null,
hints: Object.fromEntries(Object.entries(hints.params[name]).filter(([key]) =>
!['type','default'].includes(key)))
}
})
return newParams
}

export function windowBeforeUnloadFunction(e: BeforeUnloadEvent) {
e.preventDefault()
e.returnValue = ''
Expand Down
14 changes: 7 additions & 7 deletions src/store/printer/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
PrinterGetterObject,
PrinterStateLight,
} from '@/store/printer/types'
import { caseInsensitiveSort, formatFrequency, getMacroParams } from '@/plugins/helpers'
import { caseInsensitiveSort, formatFrequency, getMacroParamsFromState, getMacroParamsFromConfig } from '@/plugins/helpers'
import { RootState } from '@/store/types'
import {
mdiFan,
Expand Down Expand Up @@ -168,22 +168,22 @@ export const getters: GetterTree<PrinterState, RootState> = {
const settings = state.configfile?.settings ?? null

Object.keys(config)
.filter((prop) => prop.toLowerCase().startsWith('gcode_macro'))
.filter((prop) => prop.startsWith('gcode_macro') &&
!settings[prop.toLowerCase()]?.rename_existing)
.forEach((prop) => {
const name = prop.replace('gcode_macro ', '')
if (name.startsWith('_')) return
const variables = state[prop] ?? {}
const hints = variables?.front_end_hints

const propLower = prop.toLowerCase()
const propSettings = settings[propLower]
if ('rename_existing' in propSettings) return

const variables = state[prop] ?? {}

array.push({
name,
description: settings[propLower].description ?? null,
prop: propSettings,
params: getMacroParams(propSettings),
params: hints ? getMacroParamsFromState(hints) : getMacroParamsFromConfig(propSettings),
hidden: (hints && 'hidden' in hints) ? hints.hidden : name.startsWith('_'),
variables,
})
})
Expand Down
4 changes: 3 additions & 1 deletion src/store/printer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ export interface PrinterStateBedMesh {
}

export interface PrinterStateMacroParam {
type: 'int' | 'double' | 'string' | null
type: 'int' | 'float' | 'string' | 'select' | 'checkbox' | null
default: string | null
hints?: { [key: string] : any }
}

export type PrinterStateMacroParams = {
Expand All @@ -200,6 +201,7 @@ export interface PrinterStateMacro {
[key: string]: any
}
params: PrinterStateMacroParams
hidden: boolean
}

export interface PrinterStateKlipperConfig {
Expand Down

0 comments on commit 55a8501

Please sign in to comment.