162 lines
7 KiB
Text
162 lines
7 KiB
Text
use mod.prelude.widgets_internal.*
|
|
mod.widgets.Button.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.Button.draw_bg.border_radius = 0.0
|
|
mod.widgets.Button.draw_bg.border_size = 2.0
|
|
mod.widgets.Button.draw_bg.color_2 = #d4d0c8
|
|
// A ROW OF FIELDS IS ONE HEIGHT. Left to themselves the library's fields each
|
|
// come out at a size of their own -- in the widget catalogue's toolbar the
|
|
// search box, the number beside it and the drop down after it stood at three
|
|
// different heights -- and a sheet that reshapes the text box alone widens
|
|
// that gap instead of closing it. So the box metrics are stated ONCE here and
|
|
// handed to each field below, and a field added to the library later joins
|
|
// the row by being named here rather than by having these numbers copied
|
|
// into it.
|
|
let field_height = 24.0
|
|
let field_pad_v = mod.theme.mspace_1.top
|
|
let field_radius = 0.0
|
|
let field_border = mod.theme.beveling
|
|
|
|
// The room a Fit field takes. A minimum height rather than a height,
|
|
// because a line of text is shorter than a row, and the padding with it:
|
|
// where a field is Fit, the padding IS its height, and two fields padded
|
|
// differently part company again the moment the type grows.
|
|
let field_room = fn(w) {
|
|
w.min_height = field_height
|
|
w.padding.top = field_pad_v
|
|
w.padding.bottom = field_pad_v
|
|
}
|
|
// The box a field draws. Given the draw object and not the widget, because
|
|
// not every field draws its own box, and because writing a corner onto a
|
|
// draw struct that has no corner would add a slot its shader never reads.
|
|
let field_face = fn(d) {
|
|
d.border_radius = field_radius
|
|
d.border_size = field_border
|
|
}
|
|
// A field that states a frame and lays its own parts out inside it: a number
|
|
// with a stepper, a date, a time. The height is all these take. A padding
|
|
// here would NOT hold the line off the box -- each of them measures its
|
|
// parts against the turtle's whole rect and draws them from the content
|
|
// origin, so the padding displaces the line instead of insetting it, and the
|
|
// number came out sitting on the bottom edge with its arrows split around it.
|
|
let field_frame = fn(w) {
|
|
w.min_height = field_height
|
|
}
|
|
|
|
field_room(mod.widgets.TextInput)
|
|
field_face(mod.widgets.TextInput.draw_bg)
|
|
field_room(mod.widgets.ComboBox)
|
|
field_face(mod.widgets.ComboBox.draw_bg)
|
|
field_room(mod.widgets.DropDown)
|
|
field_face(mod.widgets.DropDown.draw_bg)
|
|
field_room(mod.widgets.DropDown2)
|
|
field_face(mod.widgets.DropDown2.draw_bg)
|
|
field_room(mod.widgets.FieldWell)
|
|
field_face(mod.widgets.FieldWell.draw_bg)
|
|
field_room(mod.widgets.TagField)
|
|
field_face(mod.widgets.TagField.draw_bg)
|
|
field_frame(mod.widgets.NumberField)
|
|
// The number's box strokes its own hairline and declares no border size.
|
|
mod.widgets.NumberField.draw_bg.border_radius = field_radius
|
|
// The drag-number's corner is written into its own shader, so it takes the
|
|
// room and keeps the box it draws.
|
|
field_frame(mod.widgets.ValueInput)
|
|
field_frame(mod.widgets.DateField)
|
|
field_frame(mod.widgets.TimeField)
|
|
// A picker is a date field in an anchor: the field inside fills the frame.
|
|
field_frame(mod.widgets.DatePicker)
|
|
field_frame(mod.widgets.DateRangePicker)
|
|
// The chrome-less input a well holds must impose no height of its own. It is
|
|
// a TextInput, so it inherits the minimum above, and a minimum as tall as the
|
|
// whole field, applied inside a well already that tall, pushed the line out
|
|
// through the bottom of the box.
|
|
mod.widgets.WellInput.min_height = 0.0
|
|
|
|
mod.widgets.Window.pass.clear_color = #d4d0c8
|
|
|
|
// Raised gray command buttons, with a depressed face while held.
|
|
mod.widgets.Button.draw_bg.pixel = fn() {
|
|
let p=self.pos*self.rect_size
|
|
let size=self.rect_size
|
|
let sunken=self.down>0.5
|
|
// The host's colour (a lit latch) fills the face inside the bevel.
|
|
let own=#d4d0c8
|
|
let fill=vec4(self.host_face(own.xyz),1.0)
|
|
|
|
let tl=min(p.x,p.y)
|
|
let br=min(size.x-p.x,size.y-p.y)
|
|
let d=min(tl,br)
|
|
let upper=tl<br
|
|
let raised_light=if d<1.0 {#ffffff}else{fill}
|
|
let raised_dark=if d<1.0 {#404040}else{#808080}
|
|
let light=if sunken {#ffffff}else{raised_light}
|
|
let dark=if sunken {if d<1.0 {#808080}else{#404040}}else{raised_dark}
|
|
let edge=if upper {if sunken {dark}else{light}}else{if sunken {light}else{dark}}
|
|
let color=if d<2.0 {edge}else{fill}
|
|
let focus_p=p-vec2(4.0)
|
|
let focus_q=size-p-vec2(4.0)
|
|
let fd=min(min(focus_p.x,focus_p.y),min(focus_q.x,focus_q.y))
|
|
let dot=fract((floor(p.x)+floor(p.y))*0.5)<0.5
|
|
let face=if self.focus>0.5 && fd>=0.0 && fd<1.0 && dot {#000000}else{color}
|
|
// The wait spinner is drawn by the face, so a style that replaces the
|
|
// face wholesale drops it and a button left waiting looks idle. The
|
|
// hover and disabled mixes are deliberately NOT restored: neither style
|
|
// lit a button under the pointer, and the label carries the disabled
|
|
// state on its own, from draw_text, which no style replaces.
|
|
if self.loading>0.0 {
|
|
let sdf=Sdf2d.viewport(p)
|
|
sdf.clear(face)
|
|
let c=size*0.5
|
|
let r=max(min(size.x,size.y)*0.5-self.loading_inset,1.0)
|
|
let a0=self.anim_time*2.0*PI
|
|
sdf.arc_round_caps(c.x,c.y,r,a0,a0+1.5*PI,self.loading_stroke)
|
|
sdf.fill(vec4(self.loading_color.rgb,self.loading_color.a*self.loading))
|
|
return sdf.result
|
|
}
|
|
return face*self.host_alpha()
|
|
}
|
|
// Sunken white edit fields, independent of the light theme's gradient material.
|
|
mod.widgets.TextInput.draw_bg.pixel = fn() {
|
|
let p=self.pos*self.rect_size
|
|
let size=self.rect_size
|
|
let sunken=true
|
|
let fill=if self.disabled>0.5 {#d4d0c8}else{#ffffff}
|
|
|
|
let tl=min(p.x,p.y)
|
|
let br=min(size.x-p.x,size.y-p.y)
|
|
let d=min(tl,br)
|
|
let upper=tl<br
|
|
let raised_light=if d<1.0 {#ffffff}else{#d4d0c8}
|
|
let raised_dark=if d<1.0 {#404040}else{#808080}
|
|
let light=if sunken {#ffffff}else{raised_light}
|
|
let dark=if sunken {if d<1.0 {#808080}else{#404040}}else{raised_dark}
|
|
let edge=if upper {if sunken {dark}else{light}}else{if sunken {light}else{dark}}
|
|
let color=if d<2.0 {edge}else{fill}
|
|
return color
|
|
}
|
|
|
|
// A white combo field and a separate raised arrow button.
|
|
mod.widgets.DropDown.draw_bg.pixel = fn() {
|
|
let p=self.pos*self.rect_size
|
|
let size=self.rect_size
|
|
let tl=min(p.x,p.y)
|
|
let br=min(size.x-p.x,size.y-p.y)
|
|
let edge=if tl<br {if tl<1.0 {#808080}else{#404040}}else{#ffffff}
|
|
let field=if min(tl,br)<2.0 {edge}else{if self.disabled>0.5 {#d4d0c8}else{#ffffff}}
|
|
let q=p-vec2(size.x-18.0,2.0)
|
|
let bs=vec2(16.0,size.y-4.0)
|
|
if q.x>=0.0 && q.y>=0.0 && q.x<bs.x && q.y<bs.y {
|
|
let a=min(q.x,q.y)
|
|
let b=min(bs.x-q.x,bs.y-q.y)
|
|
let pressed=self.down>0.5 || self.active>0.5
|
|
let face=if min(a,b)<1.0 {if (a<b)!=pressed {#ffffff}else{#808080}}else{#d4d0c8}
|
|
let ay=q.y-(bs.y*0.5-1.0)
|
|
let arrow=ay>=0.0 && ay<=3.0 && abs(q.x-bs.x*0.5)<=3.0-ay
|
|
return if arrow {if self.disabled>0.5 {#808080}else{#000000}}else{face}
|
|
}
|
|
return field
|
|
}
|
|
|
|
// Square two-line panel frames; the app keeps the same resident widgets.
|
|
mod.widgets.PanelView.draw_bg.bevel = 1.0
|
|
mod.widgets.PanelView.draw_bg.border_radius = 0.0
|
|
true
|