package widget import "fmt" import . "gitlab.com/beoran/ebsgo/zori/types" import "gitlab.com/beoran/ebsgo/zori/backend" import "gitlab.com/beoran/ebsgo/zori/state" import "gitlab.com/beoran/ebsgo/zori/style" import "gitlab.com/beoran/ebsgo/zori/event" import "gitlab.com/beoran/ebsgo/monolog" var LastID = 0 type ActionCallback func (w Widget, data ... interface{}) event.Result type Basic struct { Be backend.Backend ID int MyRoot RootWidget Box Outer Box Inner Box Z int style.BasicTheme MyParent Widget MyChildren []Widget state.Flag Result interface{} ActionCallback } func (b * Basic) SetBounds(bounds * Box) { b.Box.X = bounds.X b.Box.Y = bounds.Y b.Box.W = bounds.W b.Box.H = bounds.H b.Outer = b.Box b.Inner = b.Box } func NewBasicWithTheme(parent Widget, bounds * Box, theme * style.Theme, cb ActionCallback) * Basic { b := &Basic{} LastID++ b.ID = LastID if parent != nil { b.Be = parent.Backend() if parent.Theme() != nil { b.MyTheme = * parent.Theme() } if root, ok := parent.(RootWidget) ; ok { b.MyRoot = root } else { b.MyRoot = parent.Root() } b.MyParent = parent box := Bounds(parent.Top(), parent.Left(), parent.Width(), parent.Height()) b.SetBounds(&box) } if bounds != nil { b.SetBounds(bounds) } if theme != nil { b.MyTheme = *theme } if cb != nil { b.ActionCallback = cb } return b; } func NewBasic(parent Widget, bounds * Box, cb ActionCallback) * Basic { return NewBasicWithTheme(parent, bounds, nil, cb) } func NewBasicWidgetWithTheme(parent Widget, bounds * Box, theme * style.Theme, cb ActionCallback) Widget { return Widget(NewBasicWidgetWithTheme(parent, bounds, theme, cb)); } func NewBasicWidget(parent Widget, bounds * Box, cb ActionCallback) Widget { return Widget(NewBasicWidget(parent, bounds, cb)); } func (b Basic) Backend() backend.Backend { return b.Be } func (b Basic) Parent() Widget { return b.MyParent } func (b Basic) Root() RootWidget { return b.MyRoot } func (b Basic) Children() []Widget { return b.MyChildren } func (b * Basic) AddChild(widget Widget) Widget { b.MyChildren = append(b.MyChildren, widget) return widget } func (bw *Basic) Action(ev event.Action) event.Result { monolog.Info("Action"); if bw.ActionCallback != nil { return bw.ActionCallback(bw, ev.Data) } return event.Pass; } func (bw *Basic) Update(ev event.Update) event.Result { monolog.Info("Update"); return event.Pass; } func (bw *Basic) Draw(ev event.Draw) event.Result { monolog.Info("Draw"); return event.Pass; } func (bw *Basic) Resize(ev event.Resize) event.Result { monolog.Info("Resize"); return event.Pass; } func (bw *Basic) Destroy(ev event.Destroy) event.Result { monolog.Info("Destroy"); return event.Pass; } func (bw *Basic) Close(ev event.Close) event.Result { monolog.Info("Close"); return event.Pass; } func (bw *Basic) NewChild(ev event.NewChild) event.Result { monolog.Info("Child"); return event.Pass; } func (bw *Basic) JoystickButtonPress(ev event.Joystick) event.Result { monolog.Info("JBP"); return event.Pass; } func (bw *Basic) JoystickButtonRelease(ev event.Joystick) event.Result { monolog.Info("JBR"); return event.Pass; } func (bw *Basic) JoystickAxis(ev event.Joystick) event.Result { monolog.Info("JAX"); return event.Pass; } func (bw *Basic) KeyPress(ev event.Key) event.Result { monolog.Info("KEP"); return event.Pass; } func (bw *Basic) KeyRelease(ev event.Key) event.Result { monolog.Info("KER"); return event.Pass; } func (bw *Basic) MouseAxes(ev event.Mouse) event.Result { monolog.Info("MAX"); return event.Pass; } func (bw *Basic) MouseWarped(ev event.Mouse) event.Result { monolog.Info("MWA"); return event.Pass; } func (bw *Basic) MouseButtonPress(ev event.Mouse) event.Result { monolog.Info("MBP"); return event.Pass; } func (bw *Basic) MouseButtonRelease(ev event.Mouse) event.Result { monolog.Info("MBR"); return event.Pass; } func (bw *Basic) MouseEnterDisplay(ev event.Mouse) event.Result { monolog.Info("MED"); return event.Pass; } func (bw *Basic) MouseLeaveDisplay(ev event.Mouse) event.Result { monolog.Info("MLD"); return event.Pass; } func (bw *Basic) TouchBegin(ev event.Touch) event.Result { monolog.Info("TB"); return event.Pass; } func (bw *Basic) TouchEnd(ev event.Touch) event.Result { monolog.Info("TE"); return event.Pass; } func (bw *Basic) TouchMove(ev event.Touch) event.Result { monolog.Info("TM"); return event.Pass; } func (bw *Basic) TouchCancel(ev event.Touch) event.Result { monolog.Info("TC"); return event.Pass; } func (bw *Basic) Default(ev event.Event) event.Result { monolog.Warning("Warning using default event handler for type %d %s!", ev.Type(), event.String(ev)); return event.Pass; } func (bw *Basic) KeyChar(kc event.Key) event.Result { monolog.Info("KeyChar of Basic") return event.Done } // Checks if the coordinates are inside the main rectange of the widget. func (bw *Basic) IsInside(x, y int) bool { return (x >= bw.X) && (x <= bw.X + bw.W) && (y >= bw.Y) && (y <= (bw.Y + bw.H)) } func (bw * Basic) String() string { return fmt.Sprintf("widget %d", bw.ID) } func DispatchRecursive(bw Widget, ev event.Event) event.Result { res := event.Dispatch(ev, bw) dlev := "INFO" switch ev.(type) { case event.Draw: dlev = "DEBUG" case event.Update: dlev = "DEBUG" case event.Mouse: if ev.Type() == event.TypeMouseAxes { dlev = "DEBUG" ; } default: break } monolog.Log(dlev, "Parent dispatch: %s -> %s", event.String(ev), bw.String()) if event.Done != res { one_ok := false for _ , child := range bw.Children() { monolog.Log(dlev, "Child dispatch: %v -> %s", child, event.String(ev)) res := event.Dispatch(ev, child) if res == event.Done { one_ok = true } } if one_ok { return event.Done } else { return event.Pass } } else { return event.Done } } func (bw * Basic) Dispatch(ev event.Event) event.Result { return DispatchRecursive(bw, ev) } /** Returns whether or not a widget will even handle an event in the first place. * For example, a hidden widget won't draw, and a disabled widget won't * accept any system events, and a NULL widget doesn't accept events at all.. */ func (bw * Basic) AcceptsEvent(ev event.Event) bool { switch ev.(type) { case event.Draw: return bw.Visible() default: return bw.Active() } } func (bw * Basic) Priority() int { return bw.Z } func (bw * Basic) Compare(w Widget) int { return (w.Priority() - bw.Priority()) } /* Gets the theme to use for a marked widget. Looks up a root widget and then * looks for the cursor to determine this. */ func (bw * Basic) MarkedTheme() style.Theme { return bw.Root().MarkedTheme() } /* Gets the style to use for a hovered widget. Looks up a screen widget and then * looks for the cursor t determine this. */ func (bw * Basic) HoveredTheme() style.Theme { return bw.Root().HoveredTheme() } /* Sets the widget's margins */ func (bw * Basic) SetMargins(left, top, right, bottom int) * Basic { bw.Outer = bw.Box bw.Outer.X -= left bw.Outer.Y -= top bw.Outer.W += (left+ right) bw.Outer.H += (top + bottom) return bw } /* Sets the widget's margins, all equal */ func (bw * Basic) SetMargin(size int) * Basic { return bw.SetMargins(size, size, size, size) } /* Sets the widget's paddins */ func (bw * Basic) SetPaddings(left, top, right, bottom int) * Basic { bw.Inner = bw.Box bw.Inner.X += left bw.Inner.Y += top bw.Inner.W -= (left+ right) bw.Inner.H -= (top + bottom) return bw } /* Sets the widget's paddings, all equal */ func (bw * Basic) SetPadding(size int) * Basic { return bw.SetPaddings(size, size, size, size) } /* Draws a widget's frame based on it's style. If the background bitmap is set * then that is used, otherwise, the background color is used, including a border * if needed. */ func (bw * Basic) DrawBackground() { be := bw.Backend() dx := bw.X dy := bw.Y dw := bw.W dh := bw.H theme := bw.MyTheme if bw.Hovered() { theme = bw.HoveredTheme() } else if bw.Marked() { theme = bw.MarkedTheme() } if theme.Background.Bitmap != nil { bmp := theme.Background.Bitmap sw := bmp.Width() sh := bmp.Height() // XXX should use draw9 algorithm here be.DrawScaledBitmap(0,0,sw,sh,dx,dy,dw,dh, bmp) } else { fillcol := theme.Background.Color bordcol := theme.Border.Color thick := theme.Border.Size rad := theme.Border.Radius be.DrawRoundedSlab(dx, dy, dw, dh, rad, rad, fillcol) be.DrawRoundedRectangle(dx, dy, dw, dh, rad, rad, bordcol, thick) } } func (bw * Basic) SetState(flag state.Flag, set bool) bool { if set { bw.Flag |= flag } else { bw.Flag &= (^flag) } return bw.IsState(flag) } func (bw * Basic) IsState(flag state.Flag) bool { return ((bw.Flag & flag) == flag) } func (bw * Basic) Visible() bool { return bw != nil && ((bw.Flag & state.HIDDEN) != state.HIDDEN) } func (bw * Basic) Active() bool { return bw != nil && ((bw.Flag & state.DISABLED) != state.DISABLED ) } func (bw * Basic) Hovered() bool { return bw != nil && bw.IsState(state.HOVERED) } func (bw * Basic) Marked() bool { return bw != nil && bw.IsState(state.MARKED) } func (bw * Basic) SetHovered(set bool) bool { return bw.SetState(state.HOVERED, set) } func (bw * Basic) SetMarked(set bool) bool { return bw.SetState(state.MARKED, set) } func (bw * Basic) SetActive(set bool) bool { bw.SetState(state.DISABLED, !set) return bw.Active() } func (bw * Basic) SetVisible(set bool) bool { bw.SetState(state.HIDDEN, !set) return bw.Visible() } /** Get active and visible */ func (bw * Basic) Live() bool { return bw.Active() && bw.Visible() } /** Set active and visible */ func (bw * Basic) SetLive(live bool) { bw.SetActive(live) bw.SetVisible(live) } func (bw * Basic) FindParent(predicate func (wi Widget) bool) Widget { for parent := bw.Parent() ; parent != nil ; parent = parent.Parent() { if predicate(parent) { return parent } } return nil } func (bw * Basic) Mark() event.Result { root := bw.Root().(*Root) root.Cursors.Keyjoy.Point = bw.Box.Point return event.Done } /* Moves the widget by the offset (dx, dy). Does not move it's children. */ func (bw * Basic) MoveSelfBy(dx, dy int) { bw.Box.X += dx bw.Box.Y += dy bw.Inner.X += dx bw.Inner.Y += dy bw.Outer.X += dx bw.Outer.Y += dy } /* Resizes the widget by the offset (dw, dh). Does not move it's children. */ func (bw * Basic) ResizeSelfBy(dw, dh int) { bw.Box.W += dw bw.Box.H += dh bw.Inner.W += dw bw.Inner.H += dh bw.Outer.W += dw bw.Outer.H += dh } /* Moves the widget by the offset (dx, dy). It's children will be moved as well recursively * keeping the offset */ func (bw * Basic) MoveBy(dx, dy int) { bw.MoveSelfBy(dx, dy) for _, child := range bw.MyChildren { child.MoveBy(dx, dy) } } /* Moves the widget to (x, y). It's children will be moved as well recursively * keeping the offset */ func (bw * Basic) MoveTo(x, y int) { dx := bw.Box.X - x dy := bw.Box.Y - y bw.MoveBy(dx, dy) } /* Moves the widget to (x, y). Doesn't move children. */ func (bw * Basic) MoveSelfTo(x, y int) { dx := bw.Box.X - x dy := bw.Box.Y - y bw.MoveSelfBy(dx, dy) } /* Resizes the widget to (w, h). Doesn't resize children. Margins and padding will be * resized accordingly. */ func (bw * Basic) ResizeSelfTo(w, h int) { dx := bw.Box.W - w dy := bw.Box.H - h bw.ResizeSelfBy(dx, dy) } /* Makes the widget fit itself to all direct children. * Does not resize nor move the children. */ func (bw * Basic) FitToChildren(w, h int) { min_x := 640 min_y := 480 min_w := 0 min_h := 0 be := bw.Backend() if be.Display() != nil { min_x = be.Display().Width() min_y = be.Display().Height() } for _,child := range bw.MyChildren { if child.Left() < min_x { min_x = child.Left() } if child.Top() < min_y { min_y = child.Top() } if child.Width() > min_w { min_w = child.Width() } if child.Height() > min_h { min_w = child.Height() } } bw.MoveSelfTo(min_x, min_y) bw.ResizeSelfTo(min_w, min_h) } /* Style getters for ease of use. */ func (bw * Basic) TextFont() Font { theme := bw.MyTheme if (theme.Text.Font != nil) { return theme.Text.Font } if bw.MyParent != nil { return bw.Root().Theme().Text.Font } return nil } func (bw * Basic) TextColor() Color { theme := bw.MyTheme return theme.Text.Color } func (bw * Basic) BackgroundColor() Color { theme := bw.MyTheme return theme.Background.Color }