rui_orig/resizable.go

437 lines
11 KiB
Go
Raw Permalink Normal View History

2021-09-07 17:36:50 +03:00
package rui
import (
"fmt"
"strconv"
"strings"
)
// Constants for [Resizable] specific properties and events
2021-09-07 17:36:50 +03:00
const (
// Side is the constant for "side" property tag.
//
// Used by `Resizable`.
2024-11-13 12:56:39 +03:00
// Determines which side of the container is used to resize. The value of property is an or-combination of values listed.
// Default value is "all".
//
// Supported types: `int`, `string`.
//
// Values:
// `1`(`TopSide`) or "top" - Top frame side.
// `2`(`RightSide`) or "right" - Right frame side.
// `4`(`BottomSide`) or "bottom" - Bottom frame side.
// `8`(`LeftSide`) or "left" - Left frame side.
// `15`(`AllSides`) or "all" - All frame sides.
2021-09-07 17:36:50 +03:00
Side = "side"
// ResizeBorderWidth is the constant for "resize-border-width" property tag.
//
// Used by `Resizable`.
// Specifies the width of the resizing border.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
2021-09-07 17:36:50 +03:00
ResizeBorderWidth = "resize-border-width"
)
2021-09-07 17:36:50 +03:00
// Constants for values of [Resizable] "side" property. These constants can be ORed if needed.
const (
2021-09-07 17:36:50 +03:00
// TopSide is value of the "side" property: the top side is used to resize
TopSide = 1
2021-09-07 17:36:50 +03:00
// RightSide is value of the "side" property: the right side is used to resize
RightSide = 2
2021-09-07 17:36:50 +03:00
// BottomSide is value of the "side" property: the bottom side is used to resize
BottomSide = 4
2021-09-07 17:36:50 +03:00
// LeftSide is value of the "side" property: the left side is used to resize
LeftSide = 8
2021-09-07 17:36:50 +03:00
// AllSides is value of the "side" property: all sides is used to resize
AllSides = TopSide | RightSide | BottomSide | LeftSide
)
// Resizable represents a Resizable view
2021-09-07 17:36:50 +03:00
type Resizable interface {
View
2022-11-23 15:10:29 +03:00
ParentView
2021-09-07 17:36:50 +03:00
}
type resizableData struct {
viewData
}
// NewResizable create new Resizable object and return it
func NewResizable(session Session, params Params) Resizable {
view := new(resizableData)
view.init(session)
2021-09-07 17:36:50 +03:00
setInitParams(view, params)
return view
}
func newResizable(session Session) View {
2024-11-13 12:56:39 +03:00
return new(resizableData)
2021-09-07 17:36:50 +03:00
}
func (resizable *resizableData) init(session Session) {
resizable.viewData.init(session)
2021-09-07 17:36:50 +03:00
resizable.tag = "Resizable"
resizable.systemClass = "ruiGridLayout"
resizable.set = resizable.setFunc
resizable.changed = resizable.propertyChanged
2022-05-22 12:54:02 +03:00
}
2021-09-07 17:36:50 +03:00
func (resizable *resizableData) Views() []View {
2024-11-13 12:56:39 +03:00
if view := resizable.content(); view != nil {
return []View{view}
}
return []View{}
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
func (resizable *resizableData) content() View {
if value := resizable.getRaw(Content); value != nil {
if content, ok := value.(View); ok {
return content
2021-09-07 17:36:50 +03:00
}
}
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
func (resizable *resizableData) setFunc(tag PropertyName, value any) []PropertyName {
2021-09-07 17:36:50 +03:00
switch tag {
case Side:
return resizableSetSide(resizable, value)
2021-09-07 17:36:50 +03:00
case ResizeBorderWidth:
return setSizeProperty(resizable, tag, value)
2021-09-07 17:36:50 +03:00
case Content:
var newContent View = nil
switch value := value.(type) {
case string:
newContent = NewTextView(resizable.Session(), Params{Text: value})
2021-09-07 17:36:50 +03:00
case View:
newContent = value
case DataObject:
if newContent = CreateViewFromObject(resizable.Session(), value); newContent == nil {
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
default:
notCompatibleType(tag, value)
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
resizable.setRaw(Content, newContent)
2024-11-13 12:56:39 +03:00
return []PropertyName{}
2021-09-07 17:36:50 +03:00
case CellWidth, CellHeight, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign:
2024-11-13 12:56:39 +03:00
ErrorLogF(`Not supported "%s" property`, string(tag))
return nil
2021-09-07 17:36:50 +03:00
}
return resizable.viewData.setFunc(tag, value)
2021-09-07 17:36:50 +03:00
}
func (resizable *resizableData) propertyChanged(tag PropertyName) {
2024-11-13 12:56:39 +03:00
switch tag {
case Side:
updateInnerHTML(resizable.htmlID(), resizable.Session())
2024-11-13 12:56:39 +03:00
fallthrough
case ResizeBorderWidth:
htmlID := resizable.htmlID()
session := resizable.Session()
column, row := resizableCellSizeCSS(resizable)
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, "grid-template-columns", column)
session.updateCSSProperty(htmlID, "grid-template-rows", row)
case Content:
updateInnerHTML(resizable.htmlID(), resizable.Session())
2024-11-13 12:56:39 +03:00
default:
resizable.viewData.propertyChanged(tag)
2024-11-13 12:56:39 +03:00
}
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
func resizableSide(view View) int {
if value := view.getRaw(Side); value != nil {
2021-09-07 17:36:50 +03:00
switch value := value.(type) {
case string:
2024-11-13 12:56:39 +03:00
if value, ok := view.Session().resolveConstants(value); ok {
2021-09-07 17:36:50 +03:00
validValues := map[string]int{
"top": TopSide,
"right": RightSide,
"bottom": BottomSide,
"left": LeftSide,
"all": AllSides,
}
if strings.Contains(value, "|") {
values := strings.Split(value, "|")
sides := 0
for _, val := range values {
if n, err := strconv.Atoi(val); err == nil {
if n < 1 || n > AllSides {
return AllSides
}
sides |= n
} else if n, ok := validValues[val]; ok {
sides |= n
} else {
return AllSides
}
}
return sides
} else if n, err := strconv.Atoi(value); err == nil {
if n >= 1 || n <= AllSides {
return n
}
} else if n, ok := validValues[value]; ok {
return n
}
}
case int:
if value >= 1 && value <= AllSides {
return value
}
}
}
return AllSides
}
2024-11-13 12:56:39 +03:00
func resizableSetSide(properties Properties, value any) []PropertyName {
2021-09-07 17:36:50 +03:00
switch value := value.(type) {
case string:
if n, err := strconv.Atoi(value); err == nil {
if n >= 1 && n <= AllSides {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, n)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
validValues := map[string]int{
"top": TopSide,
"right": RightSide,
"bottom": BottomSide,
"left": LeftSide,
"all": AllSides,
}
if strings.Contains(value, "|") {
values := strings.Split(value, "|")
sides := 0
hasConst := false
for i, val := range values {
val := strings.Trim(val, " \t\r\n")
values[i] = val
if val[0] == '@' {
hasConst = true
} else if n, err := strconv.Atoi(val); err == nil {
if n < 1 || n > AllSides {
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
sides |= n
} else if n, ok := validValues[val]; ok {
sides |= n
} else {
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
}
if hasConst {
value = values[0]
for i := 1; i < len(values); i++ {
value += "|" + values[i]
}
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, value)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
}
if sides >= 1 && sides <= AllSides {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, sides)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
}
} else if value[0] == '@' {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, value)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
} else if n, ok := validValues[value]; ok {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, n)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
}
case int:
if value >= 1 && value <= AllSides {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, value)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
} else {
ErrorLogF(`Invalid value %d of "side" property`, value)
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
default:
if n, ok := isInt(value); ok {
if n >= 1 && n <= AllSides {
2024-11-13 12:56:39 +03:00
properties.setRaw(Side, n)
return []PropertyName{Side}
2021-09-07 17:36:50 +03:00
} else {
ErrorLogF(`Invalid value %d of "side" property`, n)
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
}
}
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
func resizableBorderWidth(view View) SizeUnit {
result, _ := sizeProperty(view, ResizeBorderWidth, view.Session())
2021-09-07 17:36:50 +03:00
if result.Type == Auto || result.Value == 0 {
return Px(4)
}
return result
}
2024-11-13 12:56:39 +03:00
func resizableCellSizeCSS(view View) (string, string) {
w := resizableBorderWidth(view).cssString("4px", view.Session())
side := resizableSide(view)
2021-09-07 17:36:50 +03:00
column := "1fr"
row := "1fr"
if side&LeftSide != 0 {
if (side & RightSide) != 0 {
column = w + " 1fr " + w
} else {
column = w + " 1fr"
}
} else if (side & RightSide) != 0 {
column = "1fr " + w
}
if side&TopSide != 0 {
if (side & BottomSide) != 0 {
row = w + " 1fr " + w
} else {
row = w + " 1fr"
}
} else if (side & BottomSide) != 0 {
row = "1fr " + w
}
return column, row
}
func (resizable *resizableData) cssStyle(self View, builder cssBuilder) {
2024-11-13 12:56:39 +03:00
column, row := resizableCellSizeCSS(resizable)
2021-09-07 17:36:50 +03:00
builder.add("grid-template-columns", column)
builder.add("grid-template-rows", row)
resizable.viewData.cssStyle(self, builder)
}
func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder) {
2024-11-13 12:56:39 +03:00
side := resizableSide(resizable)
2021-09-07 17:36:50 +03:00
left := 1
top := 1
leftSide := (side & LeftSide) != 0
rightSide := (side & RightSide) != 0
2024-11-13 12:56:39 +03:00
w := resizableBorderWidth(resizable).cssString("4px", resizable.Session())
2021-09-07 17:36:50 +03:00
if leftSide {
left = 2
}
writePos := func(x1, x2, y1, y2 int) {
buffer.WriteString(fmt.Sprintf(` grid-column-start: %d; grid-column-end: %d; grid-row-start: %d; grid-row-end: %d;"></div>`, x1, x2, y1, y2))
}
//htmlID := resizable.htmlID()
if (side & TopSide) != 0 {
top = 2
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, -1, event)" style="cursor: nwse-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(1, 2, 1, 2)
}
buffer.WriteString(`<div onmousedown="startResize(this, 0, -1, event)" style="cursor: ns-resize; width: 100%; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left, left+1, 1, 2)
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, -1, event)" style="cursor: nesw-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left+1, left+2, 1, 2)
}
}
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 0, event)" style="cursor: ew-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writePos(1, 2, top, top+1)
}
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 0, event)" style="cursor: ew-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writePos(left+1, left+2, top, top+1)
}
if (side & BottomSide) != 0 {
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 1, event)" style="cursor: nesw-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(1, 2, top+1, top+2)
}
buffer.WriteString(`<div onmousedown="startResize(this, 0, 1, event)" style="cursor: ns-resize; width: 100%; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left, left+1, top+1, top+2)
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 1, event)" style="cursor: nwse-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left+1, left+2, top+1, top+2)
}
}
2024-11-13 12:56:39 +03:00
if view := resizable.content(); view != nil {
2021-09-07 17:36:50 +03:00
view.addToCSSStyle(map[string]string{
"grid-column-start": strconv.Itoa(left),
"grid-column-end": strconv.Itoa(left + 1),
"grid-row-start": strconv.Itoa(top),
"grid-row-end": strconv.Itoa(top + 1),
})
2024-11-21 09:25:46 +03:00
viewHTML(view, buffer, "")
2021-09-07 17:36:50 +03:00
}
}