diff --git a/artist/patterns/border.go b/artist/patterns/border.go index 2723aa3..3202a0b 100644 --- a/artist/patterns/border.go +++ b/artist/patterns/border.go @@ -44,9 +44,7 @@ func (pattern Border) Draw (destination canvas.Canvas, clip image.Rectangle) { srcSections := nonasect(pattern.Bounds(), pattern.Inset) srcTextures := [9]Texture { } for index, section := range srcSections { - srcTextures[index] = Texture { - Canvas: canvas.Cut(pattern, section), - } + srcTextures[index].Canvas = canvas.Cut(pattern, section) } dstSections := nonasect(destination.Bounds(), pattern.Inset) diff --git a/artist/patterns/texture.go b/artist/patterns/texture.go index 5a97b8b..3c0b374 100644 --- a/artist/patterns/texture.go +++ b/artist/patterns/texture.go @@ -12,7 +12,8 @@ type Texture struct { // Draw tiles the pattern's canvas within the clipping bounds. The minimum // points of the pattern's canvas and the destination canvas will be lined up. func (pattern Texture) Draw (destination canvas.Canvas, clip image.Rectangle) { - bounds := clip.Canon().Intersect(destination.Bounds()) + realBounds := destination.Bounds() + bounds := clip.Canon().Intersect(realBounds) if bounds.Empty() { return } dstData, dstStride := destination.Buffer() @@ -23,8 +24,8 @@ func (pattern Texture) Draw (destination canvas.Canvas, clip image.Rectangle) { for x := bounds.Min.X; x < bounds.Max.X; x ++ { dstIndex := x + y * dstStride srcIndex := - wrap(x - bounds.Min.X, srcBounds.Min.X, srcBounds.Max.X) + - wrap(x - bounds.Min.Y, srcBounds.Min.Y, srcBounds.Max.Y) * srcStride + wrap(x - realBounds.Min.X, srcBounds.Min.X, srcBounds.Max.X) + + wrap(y - realBounds.Min.Y, srcBounds.Min.Y, srcBounds.Max.Y) * srcStride dstData[dstIndex] = srcData[srcIndex] }} } diff --git a/elements/basic/textbox.go b/elements/basic/textbox.go index 885cdae..1f99460 100644 --- a/elements/basic/textbox.go +++ b/elements/basic/textbox.go @@ -93,7 +93,7 @@ func (element *TextBox) HandleMouseMove (x, y int) { } func (element *TextBox) atPosition (position image.Point) int { - padding := element.theme.Padding(theme.PatternSunken) + padding := element.theme.Padding(theme.PatternInput) offset := element.Bounds().Min.Add (image.Pt ( padding[artist.SideLeft] - element.scroll, padding[artist.SideTop])) @@ -253,7 +253,7 @@ func (element *TextBox) ScrollViewportBounds () (bounds image.Rectangle) { } func (element *TextBox) scrollViewportWidth () (width int) { - padding := element.theme.Padding(theme.PatternSunken) + padding := element.theme.Padding(theme.PatternInput) return padding.Apply(element.Bounds()).Dx() } @@ -293,7 +293,7 @@ func (element *TextBox) runOnChange () { func (element *TextBox) scrollToCursor () { if !element.core.HasImage() { return } - padding := element.theme.Padding(theme.PatternSunken) + padding := element.theme.Padding(theme.PatternInput) bounds := padding.Apply(element.Bounds()) bounds = bounds.Sub(bounds.Min) bounds.Max.X -= element.valueDrawer.Em().Round() @@ -333,7 +333,7 @@ func (element *TextBox) SetConfig (new config.Config) { func (element *TextBox) updateMinimumSize () { textBounds := element.placeholderDrawer.LayoutBounds() - padding := element.theme.Padding(theme.PatternSunken) + padding := element.theme.Padding(theme.PatternInput) element.core.SetMinimumSize ( padding.Horizontal() + textBounds.Dx(), padding.Vertical() + @@ -354,8 +354,8 @@ func (element *TextBox) draw () { Disabled: !element.Enabled(), Focused: element.Focused(), } - pattern := element.theme.Pattern(theme.PatternSunken, state) - padding := element.theme.Padding(theme.PatternSunken) + pattern := element.theme.Pattern(theme.PatternInput, state) + padding := element.theme.Padding(theme.PatternInput) innerCanvas := canvas.Cut(element.core, padding.Apply(bounds)) pattern.Draw(element.core, bounds) diff --git a/theme/assets/wintergreen.png b/theme/assets/wintergreen.png new file mode 100644 index 0000000..2abe477 Binary files /dev/null and b/theme/assets/wintergreen.png differ diff --git a/theme/default.go b/theme/default.go index da0e868..ae9f482 100644 --- a/theme/default.go +++ b/theme/default.go @@ -1,6 +1,9 @@ package theme import "image" +import "bytes" +import _ "embed" +import _ "image/png" import "image/color" import "golang.org/x/image/font" import "git.tebibyte.media/sashakoshka/tomo/artist" @@ -8,6 +11,47 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/defaultfont" import "git.tebibyte.media/sashakoshka/tomo/artist/patterns" +//go:embed assets/wintergreen.png +var defaultAtlasBytes []byte +var defaultAtlas canvas.Canvas +var defaultTextures [8][10]artist.Pattern + +func atlasCell (col, row int, border artist.Inset) { + bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16)) + defaultTextures[col][row] = patterns.Border { + Canvas: canvas.Cut(defaultAtlas, bounds), + Inset: border, + } +} + +func atlasCol (col int, border artist.Inset) { + for index, _ := range defaultTextures[col] { + atlasCell(col, index, border) + } +} + +func init () { + defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes)) + defaultAtlas = canvas.FromImage(defaultAtlasImage) + + // PatternDead + atlasCol(0, artist.Inset { }) + // PatternRaised + atlasCol(1, artist.Inset { 6, 6, 6, 6 }) + // PatternSunken + atlasCol(2, artist.Inset { 4, 4, 4, 4 }) + // PatternPinboard + atlasCol(3, artist.Inset { 2, 2, 2, 2 }) + // PatternButton + atlasCol(4, artist.Inset { 6, 6, 6, 6 }) + // PatternInput + atlasCol(5, artist.Inset { 4, 4, 4, 4 }) + // PatternGutter + atlasCol(6, artist.Inset { 4, 4, 4, 4 }) + // PatternHandle + atlasCol(7, artist.Inset { 6, 6, 6, 6 }) +} + // Default is the default theme. type Default struct { } @@ -34,36 +78,52 @@ func (Default) Icon (string, IconSize, Case) canvas.Image { // Pattern returns a pattern from the default theme corresponding to the given // pattern ID. func (Default) Pattern (id Pattern, state State, c Case) artist.Pattern { + offset := 0; switch { + case state.Disabled: offset = 1 + case state.Focused && state.Pressed: offset = 6 + case state.Focused && state.On: offset = 7 + case state.Invalid && state.Pressed: offset = 8 + case state.Invalid && state.On: offset = 9 + case state.Invalid: offset = 5 + case state.Focused: offset = 4 + case state.Pressed: offset = 2 + case state.On: offset = 3 + } + switch id { - case PatternBackground: return patterns.Uhex(0x000000FF) - // case PatternDead: - // case PatternRaised: - // case PatternSunken: - // case PatternPinboard: - // case PatternButton: - // case PatternInput: - // case PatternGutter: - // case PatternHandle: - default: return patterns.Uhex(0x888888FF) + case PatternBackground: return patterns.Uhex(0xaaaaaaFF) + case PatternDead: return defaultTextures[0][offset] + case PatternRaised: return defaultTextures[1][offset] + case PatternSunken: return defaultTextures[2][offset] + case PatternPinboard: return defaultTextures[3][offset] + case PatternButton: return defaultTextures[4][offset] + case PatternInput: return defaultTextures[5][offset] + case PatternGutter: return defaultTextures[6][offset] + case PatternHandle: return defaultTextures[7][offset] + default: return patterns.Uhex(0xFF00FFFF) } } func (Default) Color (id Color, state State, c Case) color.RGBA { - switch id { - case ColorAccent: return artist.Hex(0xFF8800FF) - case ColorForeground: return artist.Hex(0xFFFFFFFF) - default: return artist.Hex(0x888888FF) + if state.Disabled { + return artist.Hex(0x444444FF) + } else { + switch id { + case ColorAccent: return artist.Hex(0x408090FF) + case ColorForeground: return artist.Hex(0x000000FF) + default: return artist.Hex(0x888888FF) + } } } // Padding returns the default padding value for the given pattern. func (Default) Padding (pattern Pattern, c Case) artist.Inset { - return artist.Inset { 4, 4, 4, 4} + return artist.Inset { 8, 8, 8, 8 } } // Margin returns the default margin value for the given pattern. func (Default) Margin (id Pattern, c Case) image.Point { - return image.Pt(4, 4) + return image.Pt(8, 8) } // Hints returns rendering optimization hints for a particular pattern.