mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-26 13:49:01 +08:00
292 Тотальный рефакторинг
This commit is contained in:
parent
ea13700279
commit
6e4c62e758
@ -6,6 +6,7 @@ import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
"sync/atomic"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// BarData is info about one bar in the chart. Every
|
||||
@ -58,7 +59,7 @@ If LegendWidth is greater than half of the chart it is not
|
||||
displayed. The same is applied to ValueWidth
|
||||
*/
|
||||
type BarChart struct {
|
||||
BaseControl
|
||||
*BaseControl
|
||||
data []BarData
|
||||
autosize bool
|
||||
gap int32
|
||||
@ -77,7 +78,7 @@ w and h - are minimal size of the control.
|
||||
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
control should keep its original size.
|
||||
*/
|
||||
func CreateBarChart(parent Control, w, h int, scale int) *BarChart {
|
||||
func CreateBarChart(parent мИнт.ИВиджет, w, h int, scale int) *BarChart {
|
||||
c := new(BarChart)
|
||||
c.BaseControl = NewBaseControl()
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// BaseControl is a base for all visible controls.
|
||||
@ -23,14 +24,14 @@ type BaseControl struct {
|
||||
tabSkip bool
|
||||
disabled bool
|
||||
hidden bool
|
||||
align мКнст.Align
|
||||
parent Control
|
||||
align мИнт.Align
|
||||
parent мИнт.ИВиджет
|
||||
inactive bool
|
||||
modal bool
|
||||
padX, padY int
|
||||
gapX, gapY int
|
||||
pack мКнст.PackType
|
||||
children []Control
|
||||
pack мИнт.PackType
|
||||
children []мИнт.ИВиджет
|
||||
mtx sync.RWMutex
|
||||
onActive func(active bool)
|
||||
style string
|
||||
@ -47,8 +48,8 @@ func nextRefId() int64 {
|
||||
}
|
||||
|
||||
//NewBaseControl --
|
||||
func NewBaseControl() BaseControl {
|
||||
return BaseControl{refID: nextRefId()}
|
||||
func NewBaseControl() *BaseControl {
|
||||
return &BaseControl{refID: nextRefId()}
|
||||
}
|
||||
|
||||
//SetClipped --
|
||||
@ -238,12 +239,12 @@ func (c *BaseControl) SetVisible(visible bool) {
|
||||
}
|
||||
|
||||
//Parent --
|
||||
func (c *BaseControl) Parent() Control {
|
||||
func (c *BaseControl) Parent() мИнт.ИВиджет {
|
||||
return c.parent
|
||||
}
|
||||
|
||||
//SetParent --
|
||||
func (c *BaseControl) SetParent(parent Control) {
|
||||
func (c *BaseControl) SetParent(parent мИнт.ИВиджет) {
|
||||
if c.parent == nil {
|
||||
c.parent = parent
|
||||
}
|
||||
@ -290,12 +291,12 @@ func (c *BaseControl) SetGaps(dx, dy int) {
|
||||
}
|
||||
|
||||
//Pack --
|
||||
func (c *BaseControl) Pack() мКнст.PackType {
|
||||
func (c *BaseControl) Pack() мИнт.PackType {
|
||||
return c.pack
|
||||
}
|
||||
|
||||
//SetPack --
|
||||
func (c *BaseControl) SetPack(pack мКнст.PackType) {
|
||||
func (c *BaseControl) SetPack(pack мИнт.PackType) {
|
||||
c.pack = pack
|
||||
}
|
||||
|
||||
@ -312,12 +313,12 @@ func (c *BaseControl) SetScale(scale int) {
|
||||
}
|
||||
|
||||
//Align --
|
||||
func (c *BaseControl) Align() мКнст.Align {
|
||||
func (c *BaseControl) Align() мИнт.Align {
|
||||
return c.align
|
||||
}
|
||||
|
||||
//SetAlign --
|
||||
func (c *BaseControl) SetAlign(align мКнст.Align) {
|
||||
func (c *BaseControl) SetAlign(align мИнт.Align) {
|
||||
c.align = align
|
||||
}
|
||||
|
||||
@ -431,9 +432,9 @@ func (c *BaseControl) ResizeChildren() {
|
||||
}
|
||||
|
||||
//AddChild --
|
||||
func (c *BaseControl) AddChild(control Control) {
|
||||
func (c *BaseControl) AddChild(control мИнт.ИВиджет) {
|
||||
if c.children == nil {
|
||||
c.children = make([]Control, 1)
|
||||
c.children = make([]мИнт.ИВиджет, 1)
|
||||
c.children[0] = control
|
||||
} else {
|
||||
if c.ChildExists(control) {
|
||||
@ -443,8 +444,8 @@ func (c *BaseControl) AddChild(control Control) {
|
||||
c.children = append(c.children, control)
|
||||
}
|
||||
|
||||
var ctrl Control
|
||||
var mainCtrl Control
|
||||
var ctrl мИнт.ИВиджет
|
||||
var mainCtrl мИнт.ИВиджет
|
||||
ctrl = c
|
||||
for ctrl != nil {
|
||||
ww, hh := ctrl.MinimalSize()
|
||||
@ -476,14 +477,14 @@ func (c *BaseControl) AddChild(control Control) {
|
||||
}
|
||||
|
||||
//Children --
|
||||
func (c *BaseControl) Children() []Control {
|
||||
child := make([]Control, len(c.children))
|
||||
func (c *BaseControl) Children() []мИнт.ИВиджет {
|
||||
child := make([]мИнт.ИВиджет, len(c.children))
|
||||
copy(child, c.children)
|
||||
return child
|
||||
}
|
||||
|
||||
//ChildExists --
|
||||
func (c *BaseControl) ChildExists(control Control) bool {
|
||||
func (c *BaseControl) ChildExists(control мИнт.ИВиджет) bool {
|
||||
if len(c.children) == 0 {
|
||||
return false
|
||||
}
|
||||
@ -576,7 +577,7 @@ func (c *BaseControl) DrawChildren() {
|
||||
defer PopClip()
|
||||
|
||||
cp := ClippedParent(c)
|
||||
var cTarget Control
|
||||
var cTarget мВид.ИВиджет
|
||||
|
||||
cTarget = c
|
||||
if cp != nil {
|
||||
@ -607,7 +608,7 @@ func (c *BaseControl) setClipper() {
|
||||
c.clipper = &rect{x: x, y: y, w: w, h: h}
|
||||
}
|
||||
//HitTest --
|
||||
func (c *BaseControl) HitTest(x, y int) мКнст.HitResult {
|
||||
func (c *BaseControl) HitTest(x, y int) мИнт.HitResult {
|
||||
if x > c.x && x < c.x+c.width-1 &&
|
||||
y > c.y && y < c.y+c.height-1 {
|
||||
return мКнст.HitInside
|
||||
@ -627,7 +628,7 @@ func (c *BaseControl) HitTest(x, y int) мКнст.HitResult {
|
||||
}
|
||||
|
||||
//ProcessEvent --
|
||||
func (c *BaseControl) ProcessEvent(ev мКнст.Event) bool {
|
||||
func (c *BaseControl) ProcessEvent(ev мИнт.ИСобытие) bool {
|
||||
return SendEventToChild(c, ev)
|
||||
}
|
||||
|
||||
@ -671,9 +672,9 @@ func (c *BaseControl) SetActiveTextColor(clr term.Attribute) {
|
||||
func (c *BaseControl) SetActiveBackColor(clr term.Attribute) {
|
||||
c.bgActive = clr
|
||||
}
|
||||
|
||||
func (c *BaseControl) removeChild(control Control) {
|
||||
children := []Control{}
|
||||
//RemoveChild --
|
||||
func (c *BaseControl) RemoveChild(control мИнт.ИВиджет) {
|
||||
children := []мИнт.ИВиджет{}
|
||||
|
||||
for _, child := range c.children {
|
||||
if child.RefID() == control.RefID() {
|
||||
@ -691,6 +692,6 @@ func (c *BaseControl) removeChild(control Control) {
|
||||
|
||||
// Destroy removes an object from its parental chain
|
||||
func (c *BaseControl) Destroy() {
|
||||
c.parent.removeChild(c)
|
||||
c.parent.RemoveChild(c)
|
||||
c.parent.SetConstraints(0, 0)
|
||||
}
|
||||
|
11
button.go
11
button.go
@ -6,6 +6,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -14,11 +15,11 @@ emits OnClick event. Event has only one valid field Sender.
|
||||
Button can be clicked with mouse or using space on keyboard while the Button is active.
|
||||
*/
|
||||
type Button struct {
|
||||
BaseControl
|
||||
*BaseControl
|
||||
shadowColor term.Attribute
|
||||
bgActive term.Attribute
|
||||
pressed int32
|
||||
onClick func(мКнст.Event)
|
||||
onClick func(мИнт.ИСобытие)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -30,7 +31,7 @@ title - button title.
|
||||
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
control should keep its original size.
|
||||
*/
|
||||
func CreateButton(parent Control, width, height int, title string, scale int) *Button {
|
||||
func CreateButton(parent мИнт.ИВиджет, width, height int, title string, scale int) *Button {
|
||||
b := new(Button)
|
||||
b.BaseControl = NewBaseControl()
|
||||
|
||||
@ -117,7 +118,7 @@ processes an event it should return true. If the method returns false it means
|
||||
that the control do not want or cannot process the event and the caller sends
|
||||
the event to the control parent
|
||||
*/
|
||||
func (b *Button) ProcessEvent(event мКнст.Event) bool {
|
||||
func (b *Button) ProcessEvent(event мИнт.ИСобытие) bool {
|
||||
if !b.Enabled() {
|
||||
return false
|
||||
}
|
||||
@ -165,6 +166,6 @@ func (b *Button) ProcessEvent(event мКнст.Event) bool {
|
||||
|
||||
// OnClick sets the callback that is called when one clicks button
|
||||
// with mouse or pressing space on keyboard while the button is active
|
||||
func (b *Button) OnClick(fn func(мКнст.Event)) {
|
||||
func (b *Button) OnClick(fn func(мИнт.ИСобытие)) {
|
||||
b.onClick = fn
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
xs "github.com/huandu/xstrings"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -11,7 +12,7 @@ CheckBox control. It can be two-state one(on and off) - it is default mode - or
|
||||
State values are 0=off, 1=on, 2=third state
|
||||
*/
|
||||
type CheckBox struct {
|
||||
BaseControl
|
||||
*BaseControl
|
||||
state int
|
||||
allow3state bool
|
||||
|
||||
@ -27,7 +28,7 @@ scale - the way of scaling the control when the parent is resized. Use DoNotScal
|
||||
control should keep its original size.
|
||||
CheckBox state can be changed using mouse or pressing space on keyboard while the control is active
|
||||
*/
|
||||
func CreateCheckBox(parent Control, width int, title string, scale int) *CheckBox {
|
||||
func CreateCheckBox(parent мИнт.ИВиджет, width int, title string, scale int) *CheckBox {
|
||||
c := new(CheckBox)
|
||||
c.BaseControl = NewBaseControl()
|
||||
c.parent = parent
|
||||
@ -102,7 +103,7 @@ func (c *CheckBox) Draw() {
|
||||
// processes an event it should return true. If the method returns false it means
|
||||
// that the control do not want or cannot process the event and the caller sends
|
||||
// the event to the control parent
|
||||
func (c *CheckBox) ProcessEvent(event мКнст.Event) bool {
|
||||
func (c *CheckBox) ProcessEvent(event мИнт.ИСобытие) bool {
|
||||
if (!c.Active() && event.Type == мКнст.EventKey) || !c.Enabled() {
|
||||
return false
|
||||
}
|
||||
|
37
composer.go
37
composer.go
@ -4,6 +4,7 @@ import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
"sync"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// Composer is a service object that manages Views and console, processes
|
||||
@ -11,9 +12,9 @@ import (
|
||||
// one object of this type
|
||||
type Composer struct {
|
||||
// list of visible Views
|
||||
windows []Control
|
||||
windows []мИнт.ИВиджет
|
||||
windowBorder мКнст.BorderStyle
|
||||
consumer Control
|
||||
consumer мИнт.ИВиджет
|
||||
// last pressed key - to make repeatable actions simpler, e.g, at first
|
||||
// one presses Ctrl+S and then just repeatedly presses arrow lest to
|
||||
// resize Window
|
||||
@ -53,7 +54,7 @@ func WindowManager() *Composer {
|
||||
// this function the control will recieve all mouse and keyboard events even
|
||||
// if it is not active or mouse is outside it. Useful to implement dragging
|
||||
// or alike stuff
|
||||
func GrabEvents(c Control) {
|
||||
func GrabEvents(c мИнт.ИВиджет) {
|
||||
comp.consumer = c
|
||||
}
|
||||
|
||||
@ -63,7 +64,7 @@ func ReleaseEvents() {
|
||||
comp.consumer = nil
|
||||
}
|
||||
|
||||
func termboxEventToLocal(ev term.Event) мКнст.Event {
|
||||
func termboxEventToLocal(ev term.Event) мИнт.ИСобытие {
|
||||
e := мКнст.Event{Type: мКнст.EventType(ev.Type), Ch: ev.Ch,
|
||||
Key: ev.Key, Err: ev.Err, X: ev.MouseX, Y: ev.MouseY,
|
||||
Mod: ev.Mod, Width: ev.Width, Height: ev.Height}
|
||||
@ -146,16 +147,16 @@ func (c *Composer) EndUpdate() {
|
||||
c.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (c *Composer) getWindowList() []Control {
|
||||
func (c *Composer) getWindowList() []мИнт.ИВиджет {
|
||||
c.mtx.RLock()
|
||||
defer c.mtx.RUnlock()
|
||||
|
||||
arr_copy := make([]Control, len(c.windows))
|
||||
arr_copy := make([]мИнт.ИВиджет, len(c.windows))
|
||||
copy(arr_copy, c.windows)
|
||||
return arr_copy
|
||||
}
|
||||
|
||||
func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (Control, мКнст.HitResult) {
|
||||
func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (мИнт.ИВиджет, мИнт.HitResult) {
|
||||
windows := c.getWindowList()
|
||||
if len(windows) == 0 {
|
||||
return nil, мКнст.HitOutside
|
||||
@ -172,7 +173,7 @@ func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (Control, мКн
|
||||
return nil, мКнст.HitOutside
|
||||
}
|
||||
|
||||
func (c *Composer) activateWindow(window Control) bool {
|
||||
func (c *Composer) activateWindow(window мИнт.ИВиджет) bool {
|
||||
windows := c.getWindowList()
|
||||
if c.topWindow() == window {
|
||||
for _, v := range windows {
|
||||
@ -256,7 +257,7 @@ func (c *Composer) moveActiveWindowToBottom() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Composer) sendEventToActiveWindow(ev мКнст.Event) bool {
|
||||
func (c *Composer) sendEventToActiveWindow(ev мИнт.ИВиджет) bool {
|
||||
view := c.topWindow()
|
||||
if view != nil {
|
||||
return view.ProcessEvent(ev)
|
||||
@ -265,7 +266,7 @@ func (c *Composer) sendEventToActiveWindow(ev мКнст.Event) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Composer) topWindow() Control {
|
||||
func (c *Composer) topWindow() мИнт.ИВиджет {
|
||||
windows := c.getWindowList()
|
||||
|
||||
if len(windows) == 0 {
|
||||
@ -275,7 +276,7 @@ func (c *Composer) topWindow() Control {
|
||||
return windows[len(windows)-1]
|
||||
}
|
||||
|
||||
func (c *Composer) resizeTopWindow(ev мКнст.Event) bool {
|
||||
func (c *Composer) resizeTopWindow(ev мИнт.ИСобытие) bool {
|
||||
view := c.topWindow()
|
||||
if view == nil {
|
||||
return false
|
||||
@ -309,7 +310,7 @@ func (c *Composer) resizeTopWindow(ev мКнст.Event) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Composer) moveTopWindow(ev мКнст.Event) bool {
|
||||
func (c *Composer) moveTopWindow(ev мИнт.ИСобытие) bool {
|
||||
view := c.topWindow()
|
||||
if view != nil {
|
||||
topwindow, ok := view.(*Window)
|
||||
@ -362,7 +363,7 @@ func (c *Composer) closeTopWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Composer) processWindowDrag(ev мКнст.Event) {
|
||||
func (c *Composer) processWindowDrag(ev мИнт.ИСобытие) {
|
||||
if ev.Mod != term.ModMotion || c.dragType == мКнст.DragNone {
|
||||
return
|
||||
}
|
||||
@ -491,7 +492,7 @@ func (c *Composer) processWindowDrag(ev мКнст.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Composer) processMouse(ev мКнст.Event) {
|
||||
func (c *Composer) processMouse(ev мИнт.ИСобытие) {
|
||||
if c.consumer != nil {
|
||||
tmp := c.consumer
|
||||
tmp.ProcessEvent(ev)
|
||||
@ -587,12 +588,12 @@ func Stop() {
|
||||
}
|
||||
|
||||
// DestroyWindow removes the Window from the list of managed Windows
|
||||
func (c *Composer) DestroyWindow(view Control) {
|
||||
func (c *Composer) DestroyWindow(view мИнт.ИВиджет) {
|
||||
ev := мКнст.Event{Type: мКнст.EventClose}
|
||||
c.sendEventToActiveWindow(ev)
|
||||
|
||||
windows := c.getWindowList()
|
||||
var newOrder []Control
|
||||
var newOrder []мИнт.ИВиджет
|
||||
for i := 0; i < len(windows); i++ {
|
||||
if windows[i] != view {
|
||||
newOrder = append(newOrder, windows[i])
|
||||
@ -622,7 +623,7 @@ func IsDeadKey(key term.Key) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Composer) processKey(ev мКнст.Event) {
|
||||
func (c *Composer) processKey(ev мИнт.ИСобытие) {
|
||||
if ev.Key == term.KeyEsc {
|
||||
if IsDeadKey(c.lastKey) {
|
||||
c.lastKey = term.KeyEsc
|
||||
@ -698,7 +699,7 @@ func (c *Composer) processKey(ev мКнст.Event) {
|
||||
}
|
||||
}
|
||||
//ProcessEvent --
|
||||
func ProcessEvent(ev мКнст.Event) {
|
||||
func ProcessEvent(ev мИнт.ИСобытие) {
|
||||
switch ev.Type {
|
||||
case мКнст.EventCloseWindow:
|
||||
comp.closeTopWindow()
|
||||
|
@ -2,11 +2,11 @@ package clui
|
||||
|
||||
import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы."
|
||||
)
|
||||
|
||||
// Control is an interface that every visible control should implement
|
||||
type Control interface {
|
||||
// TControl is an interface that every visible control should implement
|
||||
type TControl interface {
|
||||
// Title returns the current title or text of the control
|
||||
Title() string
|
||||
// SetTitle changes control text or title
|
||||
@ -44,10 +44,10 @@ type Control interface {
|
||||
SetVisible(enabled bool)
|
||||
// Parent return control's container or nil if there is no parent container
|
||||
// that is true for Windows
|
||||
Parent() Control
|
||||
Parent() мИнт.ИВиджет
|
||||
// The function should not be called manually. It is for internal use by
|
||||
// library
|
||||
SetParent(parent Control)
|
||||
SetParent(parent мИнт.ИВиджет)
|
||||
// Modal returns if a control is always on top and does not allow to
|
||||
// change the current control. Used only by Windows, for other kind of
|
||||
// controls it does nothing
|
||||
@ -66,9 +66,9 @@ type Control interface {
|
||||
SetGaps(dx, dy int)
|
||||
// Pack returns direction in which a container packs
|
||||
// its children: horizontal or vertical
|
||||
Pack() мКнст.PackType
|
||||
Pack() мИнт.PackType
|
||||
// SetPack changes the direction of children packing
|
||||
SetPack(pack мКнст.PackType)
|
||||
SetPack(pack мИнт.PackType)
|
||||
// Scale return scale coefficient that is used to calculate
|
||||
// new control size after its parent resizes.
|
||||
// Fixed means the controls never changes its size.
|
||||
@ -85,8 +85,8 @@ type Control interface {
|
||||
// See Scale method for details
|
||||
SetScale(scale int)
|
||||
// Align returns alignment of title in control
|
||||
Align() мКнст.Align
|
||||
SetAlign(align мКнст.Align)
|
||||
Align() мИнт.Align
|
||||
SetAlign(align мИнт.Align)
|
||||
|
||||
TextColor() term.Attribute
|
||||
// SetTextColor changes text color of the control.
|
||||
@ -110,12 +110,12 @@ type Control interface {
|
||||
// AddChild adds a new child to a container
|
||||
// The method should not be called manually. It is automatically called
|
||||
// if parent is not nil in Create* function
|
||||
AddChild(control Control)
|
||||
AddChild(control мИнт.ИВиджет)
|
||||
// Children returns the copy of the list of container child controls
|
||||
Children() []Control
|
||||
Children() []мИнт.ИВиджет
|
||||
// ChildExists returns true if a control has argument as one of its
|
||||
// children or child of one of the children
|
||||
ChildExists(control Control) bool
|
||||
ChildExists(control мИнт.ИВиджет) bool
|
||||
// MinimalSize returns the minimal size required by a control to show
|
||||
// it and all its children.
|
||||
MinimalSize() (w int, h int)
|
||||
@ -142,18 +142,18 @@ type Control interface {
|
||||
// HitTest returns the area that corresponds to the clicked
|
||||
// position X, Y (absolute position in console window): title,
|
||||
// internal view area, title button, border or outside the control
|
||||
HitTest(x, y int) мКнст.HitResult
|
||||
HitTest(x, y int) мИнт.HitResult
|
||||
// ProcessEvent processes all events come from the control parent. If a control
|
||||
// processes an event it should return true. If the method returns false it means
|
||||
// that the control do not want or cannot process the event and the caller sends
|
||||
// the event to the control parent
|
||||
ProcessEvent(ev мКнст.Event) bool
|
||||
ProcessEvent(ev мИнт.ИСобытие) bool
|
||||
// RefID returns the controls internal reference id
|
||||
RefID() int64
|
||||
// removeChild removes a child from a container
|
||||
// It's used to "destroy" controls whenever a control is no longer used
|
||||
// by the user
|
||||
removeChild(control Control)
|
||||
removeChild(control мИнт.ИВиджет)
|
||||
// Destroy is the public interface to remove an object from its parental chain
|
||||
// it implies this control will stop receiving events and will not be drawn nor
|
||||
// will impact on other objects position and size calculation
|
||||
|
43
ctrlutil.go
43
ctrlutil.go
@ -3,6 +3,7 @@ package clui
|
||||
import (
|
||||
мКнст "./пакКонстанты"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// ThumbPosition returns a scrollbar thumb position depending
|
||||
@ -58,7 +59,7 @@ func ItemByThumbPosition(position, itemCount, length int) int {
|
||||
// ChildAt returns the children of parent control that is at absolute
|
||||
// coordinates x, y. Returns nil if x, y are outside parent control and
|
||||
// returns parent if no child is at x, y
|
||||
func ChildAt(parent Control, x, y int) Control {
|
||||
func ChildAt(parent мИнт.ИВиджет, x, y int) мИнт.ИВиджет {
|
||||
px, py := parent.Pos()
|
||||
pw, ph := parent.Size()
|
||||
if px > x || py > y || px+pw <= x || py+ph <= y {
|
||||
@ -69,7 +70,7 @@ func ChildAt(parent Control, x, y int) Control {
|
||||
return parent
|
||||
}
|
||||
|
||||
var ctrl Control
|
||||
var ctrl мИнт.ИВиджет
|
||||
ctrl = parent
|
||||
for _, child := range parent.Children() {
|
||||
if !child.Visible() {
|
||||
@ -87,7 +88,7 @@ func ChildAt(parent Control, x, y int) Control {
|
||||
}
|
||||
|
||||
// DeactivateControls makes all children of parent inactive
|
||||
func DeactivateControls(parent Control) {
|
||||
func DeactivateControls(parent мИнт.ИВиджет) {
|
||||
for _, ctrl := range parent.Children() {
|
||||
if ctrl.Active() {
|
||||
ctrl.SetActive(false)
|
||||
@ -100,7 +101,7 @@ func DeactivateControls(parent Control) {
|
||||
|
||||
// ActivateControl makes control active and disables all other children of
|
||||
// the parent. Returns true if control was found and activated
|
||||
func ActivateControl(parent, control Control) bool {
|
||||
func ActivateControl(parent, control мИнт.ИВиджет) bool {
|
||||
DeactivateControls(parent)
|
||||
res := false
|
||||
ctrl := FindChild(parent, control)
|
||||
@ -116,8 +117,8 @@ func ActivateControl(parent, control Control) bool {
|
||||
}
|
||||
|
||||
// FindChild returns control if it is a child of the parent and nil otherwise
|
||||
func FindChild(parent, control Control) Control {
|
||||
var res Control
|
||||
func FindChild(parent, control мИнт.ИВиджет) мИнт.ИВиджет {
|
||||
var res мИнт.ИВиджет
|
||||
|
||||
if parent == control {
|
||||
return parent
|
||||
@ -139,7 +140,7 @@ func FindChild(parent, control Control) Control {
|
||||
}
|
||||
|
||||
// IsMouseClickEvent returns if a user action can be treated as mouse click.
|
||||
func IsMouseClickEvent(ev мКнст.Event) bool {
|
||||
func IsMouseClickEvent(ev мИнт.ИСобытие) bool {
|
||||
if ev.Type == мКнст.EventClick {
|
||||
return true
|
||||
}
|
||||
@ -152,7 +153,7 @@ func IsMouseClickEvent(ev мКнст.Event) bool {
|
||||
|
||||
// FindFirstControl returns the first child for that fn returns true.
|
||||
// The function is used to find active or tab-stop control
|
||||
func FindFirstControl(parent Control, fn func(Control) bool) Control {
|
||||
func FindFirstControl(parent мИнт.ИВиджет, fn func(мИнт.ИВиджет) bool) мИнт.ИВиджет {
|
||||
linear := getLinearControlList(parent, fn)
|
||||
if len(linear) == 0 {
|
||||
return nil
|
||||
@ -164,7 +165,7 @@ func FindFirstControl(parent Control, fn func(Control) bool) Control {
|
||||
// FindLastControl returns the first child for that fn returns true.
|
||||
// The function is used by TAB processing method if a user goes backwards
|
||||
// with TAB key - not supported now
|
||||
func FindLastControl(parent Control, fn func(Control) bool) Control {
|
||||
func FindLastControl(parent мИнт.ИВиджет, fn func(мИнт.ИВиджет) bool) мИнт.ИВиджет {
|
||||
linear := getLinearControlList(parent, fn)
|
||||
|
||||
if len(linear) == 0 {
|
||||
@ -176,7 +177,7 @@ func FindLastControl(parent Control, fn func(Control) bool) Control {
|
||||
|
||||
// ActiveControl returns the active child of the parent or nil if no child is
|
||||
// active
|
||||
func ActiveControl(parent Control) Control {
|
||||
func ActiveControl(parent мИнт.ИВиджет) мИнт.ИВиджет {
|
||||
fnActive := func(c Control) bool {
|
||||
return c.Active()
|
||||
}
|
||||
@ -184,7 +185,7 @@ func ActiveControl(parent Control) Control {
|
||||
}
|
||||
|
||||
// FindFirstActiveControl returns the first active control of a parent
|
||||
func FindFirstActiveControl(parent Control) Control {
|
||||
func FindFirstActiveControl(parent мИнт.ИВиджет) мИнт.ИВиджет {
|
||||
for _, curr := range getLinearControlList(parent, nil) {
|
||||
if curr.Active() {
|
||||
return curr
|
||||
@ -193,8 +194,8 @@ func FindFirstActiveControl(parent Control) Control {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLinearControlList(parent Control, fn func(Control) bool) []Control {
|
||||
result := []Control{}
|
||||
func getLinearControlList(parent мИнт.ИВиджет, fn func(мИнт.ИВиджет) bool) []мИнт.ИВиджет {
|
||||
result := []мИнт.ИВиджет{}
|
||||
|
||||
for _, curr := range parent.Children() {
|
||||
if fn != nil && fn(curr) {
|
||||
@ -216,8 +217,8 @@ func getLinearControlList(parent Control, fn func(Control) bool) []Control {
|
||||
|
||||
// NextControl returns the next or previous child (depends on next parameter)
|
||||
// that has tab-stop feature on. Used by library when processing TAB key
|
||||
func NextControl(parent Control, curr Control, next bool) Control {
|
||||
fnTab := func(c Control) bool {
|
||||
func NextControl(parent мИнт.ИВиджет, curr мИнт.ИВиджет, next bool) мИнт.ИВиджет {
|
||||
fnTab := func(c мИнт.ИВиджет) bool {
|
||||
isVisible := func() bool {
|
||||
ctrl := c.Parent()
|
||||
|
||||
@ -273,8 +274,8 @@ func NextControl(parent Control, curr Control, next bool) Control {
|
||||
// makes it active, and then sends the event to it.
|
||||
// If it is not mouse click event then it looks for the first active child and
|
||||
// sends the event to it if it is not nil
|
||||
func SendEventToChild(parent Control, ev мКнст.Event) bool {
|
||||
var child Control
|
||||
func SendEventToChild(parent мИнт.ИВиджет, ev мИнт.ИСобытие) bool {
|
||||
var child мИнт.ИВиджет
|
||||
if IsMouseClickEvent(ev) {
|
||||
child = ChildAt(parent, ev.X, ev.Y)
|
||||
if child != nil && !child.Active() {
|
||||
@ -300,7 +301,7 @@ func SendEventToChild(parent Control, ev мКнст.Event) bool {
|
||||
|
||||
// CalcClipper calculates the clipper size based on the control's size, position
|
||||
// and paddings
|
||||
func CalcClipper(c Control) (int, int, int, int) {
|
||||
func CalcClipper(c мИнт.ИВиджет) (int, int, int, int) {
|
||||
w, h := c.Size()
|
||||
x, y := c.Pos()
|
||||
px, py := c.Paddings()
|
||||
@ -314,8 +315,8 @@ func CalcClipper(c Control) (int, int, int, int) {
|
||||
}
|
||||
|
||||
// ClippedParent finds the first c parent with clipped flag
|
||||
func ClippedParent(c Control) Control {
|
||||
var clipped Control
|
||||
func ClippedParent(c мИнт.ИВиджет) мИнт.ИВиджет {
|
||||
var clipped мИнт.ИВиджет
|
||||
|
||||
ctrl := c.Parent()
|
||||
clipped = c
|
||||
@ -333,7 +334,7 @@ func ClippedParent(c Control) Control {
|
||||
}
|
||||
|
||||
// ControlInRect returns true if c is within a given rect
|
||||
func ControlInRect(c Control, x int, y int, w int, h int) bool {
|
||||
func ControlInRect(c мИнт.ИВиджет, x int, y int, w int, h int) bool {
|
||||
xx, yy := c.Pos()
|
||||
ww, hh := c.Size()
|
||||
|
||||
|
3
edit.go
3
edit.go
@ -5,10 +5,11 @@ import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
"strings"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// OnChange sets the callback that is called when EditField content is changed
|
||||
func (e *EditField) OnChange(fn func(мКнст.Event)) {
|
||||
func (e *EditField) OnChange(fn func(мИнт.ИСобытие)) {
|
||||
e.onChange = fn
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
xs "github.com/huandu/xstrings"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -28,7 +29,7 @@ type EditField struct {
|
||||
maxWidth int
|
||||
showStars bool
|
||||
|
||||
onChange func(мКнст.Event)
|
||||
onChange func(мИнт.ИСобытие)
|
||||
onKeyPress func(term.Key, rune) bool
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ type EditField struct {
|
||||
// text - text to edit.
|
||||
// scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
// control should keep its original size.
|
||||
func CreateEditField(parent Control, width int, text string, scale int) *EditField {
|
||||
func CreateEditField(parent мИнт.ИВиджет, width int, text string, scale int) *EditField {
|
||||
e := new(EditField)
|
||||
e.BaseControl = NewBaseControl()
|
||||
e.onChange = nil
|
||||
@ -74,7 +75,7 @@ processes an event it should return true. If the method returns false it means
|
||||
that the control do not want or cannot process the event and the caller sends
|
||||
the event to the control parent
|
||||
*/
|
||||
func (e *EditField) ProcessEvent(event мКнст.Event) bool {
|
||||
func (e *EditField) ProcessEvent(event мИнт.ИСобытие) bool {
|
||||
if !e.Active() || !e.Enabled() {
|
||||
return false
|
||||
}
|
||||
|
9
frame.go
9
frame.go
@ -4,6 +4,7 @@ import (
|
||||
xs "github.com/huandu/xstrings"
|
||||
"math"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -15,8 +16,8 @@ is required
|
||||
type Frame struct {
|
||||
BaseControl
|
||||
border мКнст.BorderStyle
|
||||
children []Control
|
||||
pack мКнст.PackType
|
||||
children []мИнт.ИВиджет
|
||||
pack мИнт.PackType
|
||||
scrollable bool
|
||||
lastScrollProp int
|
||||
}
|
||||
@ -30,7 +31,7 @@ bs - type of border: no border, single or double.
|
||||
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
control should keep its original size.
|
||||
*/
|
||||
func CreateFrame(parent Control, width, height int, bs мКнст.BorderStyle, scale int) *Frame {
|
||||
func CreateFrame(parent мИнт.ИВиджет, width, height int, bs мКнст.BorderStyle, scale int) *Frame {
|
||||
f := new(Frame)
|
||||
f.BaseControl = NewBaseControl()
|
||||
|
||||
@ -174,7 +175,7 @@ func (f *Frame) ScrollTo(x int, y int) {
|
||||
f.PlaceChildren()
|
||||
}
|
||||
//ProcessEvent --
|
||||
func (f *Frame) ProcessEvent(ev мКнст.Event) bool {
|
||||
func (f *Frame) ProcessEvent(ev мИнт.ИСобытие) bool {
|
||||
if ev.Type != мКнст.EventActivateChild || (!f.scrollable || ev.Target == nil) {
|
||||
return false
|
||||
}
|
||||
|
9
label.go
9
label.go
@ -3,6 +3,7 @@ package clui
|
||||
import (
|
||||
xs "github.com/huandu/xstrings"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -17,7 +18,7 @@ type Label struct {
|
||||
BaseControl
|
||||
direction мКнст.Direction
|
||||
multiline bool
|
||||
textDisplay мКнст.Align
|
||||
textDisplay мИнт.Align
|
||||
}
|
||||
|
||||
/*
|
||||
@ -29,7 +30,7 @@ title - is Label title.
|
||||
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
control should keep its original size.
|
||||
*/
|
||||
func CreateLabel(parent Control, w, h int, title string, scale int) *Label {
|
||||
func CreateLabel(parent мИнт.ИВиджет, w, h int, title string, scale int) *Label {
|
||||
c := new(Label)
|
||||
c.BaseControl = NewBaseControl()
|
||||
|
||||
@ -156,7 +157,7 @@ func (l *Label) SetMultiline(multi bool) {
|
||||
// - AlignLeft - the head of the title is shown
|
||||
// - AlignRight - the tail of the title is shown
|
||||
// The property is used only by single line Label
|
||||
func (l *Label) TextDisplay() мКнст.Align {
|
||||
func (l *Label) TextDisplay() мИнт.Align {
|
||||
return l.textDisplay
|
||||
}
|
||||
|
||||
@ -164,7 +165,7 @@ func (l *Label) TextDisplay() мКнст.Align {
|
||||
// is longer than the lable. Only AlignLeft and AlignRigth are valid values
|
||||
// for the property. Any other value does is skipped and does not affect
|
||||
// displaying the title
|
||||
func (l *Label) SetTextDisplay(align мКнст.Align) {
|
||||
func (l *Label) SetTextDisplay(align мИнт.Align) {
|
||||
if align != мКнст.AlignLeft && align != мКнст.AlignRight {
|
||||
return
|
||||
}
|
||||
|
11
listbox.go
11
listbox.go
@ -4,6 +4,7 @@ import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
"strings"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -25,7 +26,7 @@ type ListBox struct {
|
||||
topLine int
|
||||
buttonPos int
|
||||
|
||||
onSelectItem func(мКнст.Event)
|
||||
onSelectItem func(мИнт.ИСобытие)
|
||||
onKeyPress func(term.Key) bool
|
||||
}
|
||||
|
||||
@ -37,7 +38,7 @@ width and heigth - are minimal size of the control.
|
||||
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
|
||||
control should keep its original size.
|
||||
*/
|
||||
func CreateListBox(parent Control, width, height int, scale int) *ListBox {
|
||||
func CreateListBox(parent мИнт.ИВиджет, width, height int, scale int) *ListBox {
|
||||
l := new(ListBox)
|
||||
l.BaseControl = NewBaseControl()
|
||||
|
||||
@ -247,7 +248,7 @@ func (l *ListBox) Clear() {
|
||||
l.topLine = 0
|
||||
}
|
||||
|
||||
func (l *ListBox) processMouseClick(ev мКнст.Event) bool {
|
||||
func (l *ListBox) processMouseClick(ev мИнт.ИСобытие) bool {
|
||||
if ev.Key != term.MouseLeft {
|
||||
return false
|
||||
}
|
||||
@ -310,7 +311,7 @@ processes an event it should return true. If the method returns false it means
|
||||
that the control do not want or cannot process the event and the caller sends
|
||||
the event to the control parent
|
||||
*/
|
||||
func (l *ListBox) ProcessEvent(event мКнст.Event) bool {
|
||||
func (l *ListBox) ProcessEvent(event мИнт.ИСобытие) bool {
|
||||
if !l.Active() || !l.Enabled() {
|
||||
return false
|
||||
}
|
||||
@ -456,7 +457,7 @@ func (l *ListBox) RemoveItem(id int) bool {
|
||||
|
||||
// OnSelectItem sets a callback that is called every time
|
||||
// the selected item is changed
|
||||
func (l *ListBox) OnSelectItem(fn func(мКнст.Event)) {
|
||||
func (l *ListBox) OnSelectItem(fn func(мИнт.ИСобытие)) {
|
||||
l.onSelectItem = fn
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package clui
|
||||
import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// Composer is a service object that manages Views and console, processes
|
||||
@ -10,7 +11,7 @@ import (
|
||||
// one object of this type
|
||||
type mainLoop struct {
|
||||
// a channel to communicate with View(e.g, Views send redraw event to this channel)
|
||||
channel chan мКнст.Event
|
||||
channel chan мИнт.ИСобытие
|
||||
}
|
||||
|
||||
var (
|
||||
@ -19,7 +20,7 @@ var (
|
||||
|
||||
func initMainLoop() {
|
||||
loop = new(mainLoop)
|
||||
loop.channel = make(chan мКнст.Event)
|
||||
loop.channel = make(chan мИнт.ИСобытие)
|
||||
}
|
||||
|
||||
// MainLoop starts the main application event loop
|
||||
@ -53,12 +54,12 @@ func MainLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func _putEvent(ev мКнст.Event) {
|
||||
func _putEvent(ev мИнт.ИСобытие) {
|
||||
loop.channel <- ev
|
||||
}
|
||||
|
||||
// PutEvent send event to a Composer directly.
|
||||
// Used by Views to ask for repainting or for quitting the application
|
||||
func PutEvent(ev мКнст.Event) {
|
||||
func PutEvent(ev мИнт.ИСобытие) {
|
||||
go _putEvent(ev)
|
||||
}
|
||||
|
3
radio.go
3
radio.go
@ -4,6 +4,7 @@ import (
|
||||
xs "github.com/huandu/xstrings"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -104,7 +105,7 @@ that the control do not want or cannot process the event and the caller sends
|
||||
the event to the control parent.
|
||||
The control processes only space button and mouse clicks to make control selected. Deselecting control is not possible: one has to click another radio of the radio group to deselect this button
|
||||
*/
|
||||
func (c *Radio) ProcessEvent(event мКнст.Event) bool {
|
||||
func (c *Radio) ProcessEvent(event мИнт.ИСобытие) bool {
|
||||
if (!c.Active() && event.Type == мКнст.EventKey) || !c.Enabled() {
|
||||
return false
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мКнст "./пакКонстанты"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -78,7 +79,7 @@ type TableView struct {
|
||||
type Column struct {
|
||||
Title string
|
||||
Width int
|
||||
Alignment мКнст.Align
|
||||
Alignment мИнт.Align
|
||||
Fg, Bg term.Attribute
|
||||
Sort мКнст.SortOrder
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
мКнст "./пакКонстанты"
|
||||
xs "github.com/huandu/xstrings"
|
||||
term "github.com/nsf/termbox-go"
|
||||
мИнт "./пакИнтерфейсы"
|
||||
)
|
||||
|
||||
// Window is an implemetation of View managed by Composer.
|
||||
@ -22,15 +23,15 @@ type Window struct {
|
||||
fixedSize bool
|
||||
border мКнст.BorderStyle
|
||||
|
||||
onClose func(мКнст.Event) bool
|
||||
onScreenResize func(мКнст.Event)
|
||||
onClose func(мИнт.ИСобытие) bool
|
||||
onScreenResize func(мИнт.ИСобытие)
|
||||
|
||||
onKeyDown *keyDownCb
|
||||
}
|
||||
|
||||
type keyDownCb struct {
|
||||
data interface{}
|
||||
fn func(evt мКнст.Event, data interface{}) bool
|
||||
fn func(evt мИнт.ИСобытие, data interface{}) bool
|
||||
}
|
||||
|
||||
//CreateWindow --
|
||||
|
@ -2,7 +2,7 @@ package пакКонстанты
|
||||
|
||||
import (
|
||||
term "github.com/nsf/termbox-go"
|
||||
мВид "../пакВиджеты"
|
||||
мИнт "../пакИнтерфейсы"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -27,19 +27,14 @@ type (
|
||||
// ViewButton is a set of buttons displayed in a view title
|
||||
ViewButton int
|
||||
// HitResult is a type of a view area that is under mouse cursor.
|
||||
// Used in mouse click events
|
||||
HitResult int
|
||||
// Align is text align: left, right and center
|
||||
Align int
|
||||
|
||||
// EventType is a type of event fired by an object
|
||||
EventType int
|
||||
// Direction indicates the direction in which a control must draw its
|
||||
// content. At that moment it can be applied to Label (text output
|
||||
// direction and to ProgressBar (direction of bar filling)
|
||||
Direction int
|
||||
// PackType sets how to pack controls inside its parent. Can be Vertical or
|
||||
// Horizontal
|
||||
PackType int
|
||||
|
||||
// SelectDialogType sets the way of choosing an item from a list for
|
||||
// SelectionDialog control: a list-based selections, or radio group one
|
||||
SelectDialogType uint
|
||||
@ -72,33 +67,6 @@ const (
|
||||
DragResizeTopRight
|
||||
)
|
||||
|
||||
// Event is structure used by Views and controls to communicate with Composer
|
||||
// and vice versa
|
||||
type Event struct {
|
||||
// Event type - the first events are mapped to termbox Event and then a few
|
||||
// own events added to the end
|
||||
Type EventType
|
||||
// Mod - is a key modifier. Only Alt modifier is supported
|
||||
Mod term.Modifier
|
||||
// Msg is a text part of the event. Used by few events: e.g, ListBox click
|
||||
// sends a value of clicked item
|
||||
Msg string
|
||||
// X and Y are multi-purpose fields: mouse coordinated for click event,
|
||||
// X is used to indicate on/off for events like Activate
|
||||
// Y is used for vertical-based events like ListBox item selection - id of the item
|
||||
X, Y int
|
||||
// Err is error got from termbox library
|
||||
Err error
|
||||
// Key is a pressed key
|
||||
Key term.Key
|
||||
// Ch is a printable representation of pressed key combinaton
|
||||
Ch rune
|
||||
// For resize event - new terminal size
|
||||
Width int
|
||||
Height int
|
||||
Target мВид.ИВиджет
|
||||
}
|
||||
|
||||
// BorderStyle constants
|
||||
const (
|
||||
BorderAuto BorderStyle = iota - 1
|
||||
@ -130,7 +98,7 @@ const (
|
||||
|
||||
// HitResult constants
|
||||
const (
|
||||
HitOutside HitResult = iota
|
||||
HitOutside мИнт.HitResult = iota
|
||||
HitInside
|
||||
HitBorder
|
||||
HitTop
|
||||
@ -160,7 +128,7 @@ const (
|
||||
|
||||
// Alignment constants
|
||||
const (
|
||||
AlignLeft Align = iota
|
||||
AlignLeft мИнт.Align = iota
|
||||
AlignRight
|
||||
AlignCenter
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user