2021-09-07 17:36:50 +03:00
package rui
2022-07-31 15:37:26 +03:00
import (
2024-11-25 23:13:29 +03:00
"fmt"
2022-07-31 15:37:26 +03:00
"strings"
)
2021-09-07 17:36:50 +03:00
2024-09-12 14:05:11 +03:00
// Constants for [Popup] specific properties and events
2021-09-07 17:36:50 +03:00
const (
2024-09-18 13:50:06 +03:00
// Title is the constant for "title" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup, TabsLayout.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Usage in Popup:
2024-09-18 13:50:06 +03:00
// Define the title.
//
2024-12-05 20:15:39 +03:00
// Supported types: string.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Usage in TabsLayout:
// Set the title of the tab. The property is set for the child view of TabsLayout.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Supported types: string.
2021-09-07 17:36:50 +03:00
Title = "title"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// TitleStyle is the constant for "title-style" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set popup title style. Default title style is "ruiPopupTitle".
//
2024-12-05 20:15:39 +03:00
// Supported types: string.
2024-11-13 12:56:39 +03:00
TitleStyle PropertyName = "title-style"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// CloseButton is the constant for "close-button" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
// Controls whether a close button can be added to the popup. Default value is false.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Supported types: bool, int, string.
2024-09-18 13:50:06 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - true, 1, "true", "yes", "on", "1" - Close button will be added to a title bar of a window.
// - false, 0, "false", "no", "off", "0" - Popup without a close button.
2024-11-13 12:56:39 +03:00
CloseButton PropertyName = "close-button"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// OutsideClose is the constant for "outside-close" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
// Controls whether popup can be closed by clicking outside of the window. Default value is false.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Supported types: bool, int, string.
2024-09-18 13:50:06 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - true, 1, "true", "yes", "on", "1" - Clicking outside the popup window will automatically call the Dismiss() method.
// - false, 0, "false", "no", "off", "0" - Clicking outside the popup window has no effect.
2024-11-13 12:56:39 +03:00
OutsideClose PropertyName = "outside-close"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// Buttons is the constant for "buttons" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Buttons that will be placed at the bottom of the popup.
//
2024-12-05 20:15:39 +03:00
// Supported types: PopupButton, []PopupButton.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is []PopupButton, other types converted to it during assignment.
// See PopupButton description for more details.
2024-11-13 12:56:39 +03:00
Buttons PropertyName = "buttons"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// ButtonsAlign is the constant for "buttons-align" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set the horizontal alignment of popup buttons.
//
2024-12-05 20:15:39 +03:00
// Supported types: int, string.
2024-09-18 13:50:06 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - 0 (LeftAlign) or "left" - Left alignment.
// - 1 (RightAlign) or "right" - Right alignment.
// - 2 (CenterAlign) or "center" - Center alignment.
// - 3 (StretchAlign) or "stretch" - Width alignment.
2024-11-13 12:56:39 +03:00
ButtonsAlign PropertyName = "buttons-align"
2022-07-08 13:16:42 +03:00
2024-09-18 13:50:06 +03:00
// DismissEvent is the constant for "dismiss-event" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
// Used to track the closing state of the Popup. It occurs after the Popup disappears from the screen.
2024-09-18 13:50:06 +03:00
//
// General listener format:
2024-12-05 20:15:39 +03:00
//
// func(popup rui.Popup)
2024-09-18 13:50:06 +03:00
//
// where:
// popup - Interface of a popup which generated this event.
//
// Allowed listener formats:
2024-12-05 20:15:39 +03:00
//
// func()
2024-11-13 12:56:39 +03:00
DismissEvent PropertyName = "dismiss-event"
2022-07-27 20:31:57 +03:00
2024-09-18 13:50:06 +03:00
// Arrow is the constant for "arrow" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Add an arrow to popup. Default value is "none".
//
2024-12-05 20:15:39 +03:00
// Supported types: int, string.
2024-09-18 13:50:06 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - 0 (NoneArrow) or "none" - No arrow.
// - 1 (TopArrow) or "top" - Arrow at the top side of the pop-up window.
// - 2 (RightArrow) or "right" - Arrow on the right side of the pop-up window.
// - 3 (BottomArrow) or "bottom" - Arrow at the bottom of the pop-up window.
// - 4 (LeftArrow) or "left" - Arrow on the left side of the pop-up window.
2024-11-13 12:56:39 +03:00
Arrow PropertyName = "arrow"
2022-07-31 15:37:26 +03:00
2024-09-18 13:50:06 +03:00
// ArrowAlign is the constant for "arrow-align" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set the horizontal alignment of the popup arrow. Default value is "center".
//
2024-12-05 20:15:39 +03:00
// Supported types: int, string.
2024-09-18 13:50:06 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - 0 (TopAlign/LeftAlign) or "top" - Top/left alignment.
// - 1 (BottomAlign/RightAlign) or "bottom" - Bottom/right alignment.
// - 2 (CenterAlign) or "center" - Center alignment.
2024-11-13 12:56:39 +03:00
ArrowAlign PropertyName = "arrow-align"
2022-07-31 15:37:26 +03:00
2024-09-18 13:50:06 +03:00
// ArrowSize is the constant for "arrow-size" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set the size(length) of the popup arrow. Default value is 16px defined by @ruiArrowSize constant.
//
2024-12-05 20:15:39 +03:00
// Supported types: SizeUnit, SizeFunc, string, float, int.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is SizeUnit, other types converted to it during assignment.
// See SizeUnit description for more details.
2024-11-13 12:56:39 +03:00
ArrowSize PropertyName = "arrow-size"
2022-07-31 15:37:26 +03:00
2024-09-18 13:50:06 +03:00
// ArrowWidth is the constant for "arrow-width" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set the width of the popup arrow. Default value is 16px defined by @ruiArrowWidth constant.
//
2024-12-05 20:15:39 +03:00
// Supported types: SizeUnit, SizeFunc, string, float, int.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is SizeUnit, other types converted to it during assignment.
// See SizeUnit description for more details.
2024-11-13 12:56:39 +03:00
ArrowWidth PropertyName = "arrow-width"
2022-08-07 18:59:56 +03:00
2024-11-25 23:13:29 +03:00
// ShowTransform is the constant for "show-transform" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-11-25 23:13:29 +03:00
// Specify start translation, scale and rotation over x, y and z axes as well as a distortion
// for an animated Popup showing/hidding.
//
2024-12-05 20:15:39 +03:00
// Supported types: TransformProperty, string.
2024-11-25 23:13:29 +03:00
//
2024-12-05 20:15:39 +03:00
// See TransformProperty description for more details.
2024-11-25 23:13:29 +03:00
//
// Conversion rules:
2024-12-05 20:15:39 +03:00
// - TransformProperty - stored as is, no conversion performed.
// - string - string representation of Transform interface. Example:
//
// "_{ translate-x = 10px, scale-y = 1.1}"
2024-11-25 23:13:29 +03:00
ShowTransform = "show-transform"
// ShowDuration is the constant for "show-duration" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-11-25 23:13:29 +03:00
// Sets the length of time in seconds that a Popup show/hide animation takes to complete.
//
2024-12-05 20:15:39 +03:00
// Supported types: float, int, string.
2024-11-25 23:13:29 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is float, other types converted to it during assignment.
2024-11-25 23:13:29 +03:00
ShowDuration = "show-duration"
// ShowTiming is the constant for "show-timing" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-11-25 23:13:29 +03:00
// Set how a Popup show/hide animation progresses through the duration of each cycle.
//
2024-12-05 20:15:39 +03:00
// Supported types: string.
2024-11-25 23:13:29 +03:00
//
// Values:
2024-12-05 20:15:39 +03:00
// - "ease" (EaseTiming) - Speed increases towards the middle and slows down at the end.
// - "ease-in" (EaseInTiming) - Speed is slow at first, but increases in the end.
// - "ease-out" (EaseOutTiming) - Speed is fast at first, but decreases in the end.
// - "ease-in-out" (EaseInOutTiming) - Speed is slow at first, but quickly increases and at the end it decreases again.
// - "linear" (LinearTiming) - Constant speed.
// - "step(n)" (StepTiming(n int) function) - Timing function along stepCount stops along the transition, displaying each stop for equal lengths of time.
// - "cubic-bezier(x1, y1, x2, y2)" (CubicBezierTiming(x1, y1, x2, y2 float64) function) - Cubic-Bezier curve timing function. x1 and x2 must be in the range [0, 1].
2024-11-25 23:13:29 +03:00
ShowTiming = "show-timing"
2024-11-26 12:56:52 +03:00
// ShowOpacity is the constant for "show-opacity" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-11-26 12:56:52 +03:00
// In [1..0] range sets the start opacity of Popup show animation (the finish animation opacity is 1).
// Opacity is the degree to which content behind the view is hidden, and is the opposite of transparency.
//
2024-12-05 20:15:39 +03:00
// Supported types: float, int, string.
2024-11-26 12:56:52 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is float, other types converted to it during assignment.
2024-11-25 23:13:29 +03:00
ShowOpacity = "show-opacity"
2024-09-18 13:50:06 +03:00
// ArrowOffset is the constant for "arrow-offset" property tag.
//
2024-12-05 20:15:39 +03:00
// Used by Popup.
2024-09-18 13:50:06 +03:00
// Set the offset of the popup arrow.
//
2024-12-05 20:15:39 +03:00
// Supported types: SizeUnit, SizeFunc, string, float, int.
2024-09-18 13:50:06 +03:00
//
2024-12-05 20:15:39 +03:00
// Internal type is SizeUnit, other types converted to it during assignment.
// See SizeUnit description for more details.
2024-11-13 12:56:39 +03:00
ArrowOffset PropertyName = "arrow-offset"
2022-07-31 15:37:26 +03:00
2022-08-07 18:59:56 +03:00
// NoneArrow is value of the popup "arrow" property: no arrow
NoneArrow = 0
// TopArrow is value of the popup "arrow" property:
// Arrow at the top side of the pop-up window
TopArrow = 1
// RightArrow is value of the popup "arrow" property:
// Arrow on the right side of the pop-up window
RightArrow = 2
// BottomArrow is value of the popup "arrow" property:
// Arrow at the bottom of the pop-up window
2022-07-31 15:37:26 +03:00
BottomArrow = 3
2022-08-07 18:59:56 +03:00
// LeftArrow is value of the popup "arrow" property:
// Arrow on the left side of the pop-up window
LeftArrow = 4
2024-09-12 14:05:11 +03:00
)
2023-05-15 15:27:37 +03:00
2024-09-12 14:05:11 +03:00
// Constants which are used as a values of [PopupButtonType] variables
const (
2023-05-15 15:27:37 +03:00
// NormalButton is the constant of the popup button type: the normal button
NormalButton PopupButtonType = 0
2024-09-18 13:50:06 +03:00
2023-05-15 15:27:37 +03:00
// DefaultButton is the constant of the popup button type: button that fires when the "Enter" key is pressed
DefaultButton PopupButtonType = 1
2024-09-18 13:50:06 +03:00
2023-05-15 15:27:37 +03:00
// CancelButton is the constant of the popup button type: button that fires when the "Escape" key is pressed
CancelButton PopupButtonType = 2
2021-09-07 17:36:50 +03:00
)
2024-09-12 14:05:11 +03:00
// PopupButtonType represent popup button type
2023-05-15 15:27:37 +03:00
type PopupButtonType int
2022-07-08 13:16:42 +03:00
// PopupButton describes a button that will be placed at the bottom of the window.
2021-09-07 17:36:50 +03:00
type PopupButton struct {
2024-09-12 14:05:11 +03:00
// Title of the button
Title string
// Type of the button
Type PopupButtonType
// OnClick is the handler function that gets called when the button is pressed
2021-09-07 17:36:50 +03:00
OnClick func ( Popup )
}
2024-09-12 14:05:11 +03:00
// Popup represents a Popup view
2021-09-07 17:36:50 +03:00
type Popup interface {
2024-09-12 14:05:11 +03:00
// View returns a content view of the popup
2021-09-07 17:36:50 +03:00
View ( ) View
2024-09-12 14:05:11 +03:00
// Session returns current client session
2021-09-07 17:36:50 +03:00
Session ( ) Session
2024-09-12 14:05:11 +03:00
// Show displays a popup
2021-09-07 17:36:50 +03:00
Show ( )
2024-09-12 14:05:11 +03:00
// Dismiss closes a popup
2021-09-07 17:36:50 +03:00
Dismiss ( )
2024-09-12 14:05:11 +03:00
2022-07-08 13:16:42 +03:00
onDismiss ( )
2021-09-07 17:36:50 +03:00
html ( buffer * strings . Builder )
viewByHTMLID ( id string ) View
2023-05-15 15:27:37 +03:00
keyEvent ( event KeyEvent ) bool
2024-11-25 23:13:29 +03:00
showAnimation ( )
dissmissAnimation ( listener func ( PropertyName ) ) bool
2021-09-07 17:36:50 +03:00
}
type popupData struct {
2024-11-25 23:13:29 +03:00
layerView GridLayout
popupView GridLayout
contentView View
2023-05-15 15:27:37 +03:00
buttons [ ] PopupButton
cancelable bool
2021-11-17 12:32:37 +03:00
dismissListener [ ] func ( Popup )
2024-11-25 23:13:29 +03:00
showTransform TransformProperty
showOpacity float64
showDuration float64
showTiming string
2021-09-07 17:36:50 +03:00
}
type popupManager struct {
popups [ ] Popup
}
2022-08-07 18:59:56 +03:00
type popupArrow struct {
column , row int
location , align int
size , width , off SizeUnit
}
func ( arrow * popupArrow ) fixOff ( popupView View ) {
if arrow . align == CenterAlign && arrow . off . Type == Auto {
2022-08-31 22:17:46 +03:00
r := GetRadius ( popupView )
2022-08-07 18:59:56 +03:00
switch arrow . location {
case TopArrow :
switch arrow . align {
case LeftAlign :
arrow . off = r . TopLeftX
case RightAlign :
arrow . off = r . TopRightX
}
case BottomArrow :
switch arrow . align {
case LeftAlign :
arrow . off = r . BottomLeftX
case RightAlign :
arrow . off = r . BottomRightX
}
case RightArrow :
switch arrow . align {
case TopAlign :
arrow . off = r . TopRightY
case BottomAlign :
arrow . off = r . BottomRightY
}
case LeftArrow :
switch arrow . align {
case TopAlign :
arrow . off = r . TopLeftY
case BottomAlign :
arrow . off = r . BottomLeftY
}
}
}
}
func ( arrow * popupArrow ) createView ( popupView View ) View {
session := popupView . Session ( )
defaultSize := func ( constTag string , defValue SizeUnit ) SizeUnit {
if value , ok := session . Constant ( constTag ) ; ok {
if size , ok := StringToSizeUnit ( value ) ; ok && size . Type != Auto && size . Value != 0 {
return size
}
}
return defValue
}
if arrow . size . Type == Auto || arrow . size . Value == 0 {
arrow . size = defaultSize ( "ruiArrowSize" , Px ( 16 ) )
}
if arrow . width . Type == Auto || arrow . width . Value == 0 {
arrow . width = defaultSize ( "ruiArrowWidth" , Px ( 16 ) )
}
2022-08-31 22:17:46 +03:00
params := Params { BackgroundColor : GetBackgroundColor ( popupView ) }
2022-08-08 17:00:49 +03:00
2024-12-03 10:25:55 +03:00
if shadow := GetShadowPropertys ( popupView ) ; shadow != nil {
2022-08-08 17:00:49 +03:00
params [ Shadow ] = shadow
}
2022-08-31 22:17:46 +03:00
if filter := GetBackdropFilter ( popupView ) ; filter != nil {
2022-08-08 17:00:49 +03:00
params [ BackdropFilter ] = filter
}
2022-08-07 18:59:56 +03:00
switch arrow . location {
case TopArrow :
params [ Row ] = 0
params [ Column ] = 1
2024-12-06 18:38:43 +03:00
params [ Clip ] = NewPolygonClip ( [ ] any { "0%" , "100%" , "50%" , "0%" , "100%" , "100%" } )
2022-08-08 17:00:49 +03:00
params [ Width ] = arrow . width
params [ Height ] = arrow . size
2022-08-07 18:59:56 +03:00
case RightArrow :
params [ Row ] = 1
params [ Column ] = 0
2024-12-06 18:38:43 +03:00
params [ Clip ] = NewPolygonClip ( [ ] any { "0%" , "0%" , "100%" , "50%" , "0%" , "100%" } )
2022-08-08 17:00:49 +03:00
params [ Width ] = arrow . size
params [ Height ] = arrow . width
2022-08-07 18:59:56 +03:00
case BottomArrow :
params [ Row ] = 0
params [ Column ] = 1
2024-12-06 18:38:43 +03:00
params [ Clip ] = NewPolygonClip ( [ ] any { "0%" , "0%" , "50%" , "100%" , "100%" , "0%" } )
2022-08-08 17:00:49 +03:00
params [ Width ] = arrow . width
params [ Height ] = arrow . size
2022-08-07 18:59:56 +03:00
case LeftArrow :
params [ Row ] = 1
params [ Column ] = 0
2024-12-06 18:38:43 +03:00
params [ Clip ] = NewPolygonClip ( [ ] any { "100%" , "0%" , "0%" , "50%" , "100%" , "100%" } )
2022-08-08 17:00:49 +03:00
params [ Width ] = arrow . size
params [ Height ] = arrow . width
2022-08-07 18:59:56 +03:00
}
arrowView := NewView ( session , params )
params = Params {
Row : arrow . row ,
Column : arrow . column ,
Content : arrowView ,
}
arrow . fixOff ( popupView )
switch arrow . location {
case TopArrow , BottomArrow :
cellWidth := make ( [ ] SizeUnit , 3 )
switch arrow . align {
case LeftAlign :
cellWidth [ 0 ] = arrow . off
cellWidth [ 2 ] = Fr ( 1 )
case RightAlign :
cellWidth [ 0 ] = Fr ( 1 )
cellWidth [ 2 ] = arrow . off
default :
cellWidth [ 0 ] = Fr ( 1 )
cellWidth [ 2 ] = Fr ( 1 )
if arrow . off . Type != Auto && arrow . off . Value != 0 {
arrowView . Set ( MarginLeft , arrow . off )
}
}
params [ CellWidth ] = cellWidth
case RightArrow , LeftArrow :
cellHeight := make ( [ ] SizeUnit , 3 )
switch arrow . align {
case TopAlign :
cellHeight [ 0 ] = arrow . off
cellHeight [ 2 ] = Fr ( 1 )
case BottomAlign :
cellHeight [ 0 ] = Fr ( 1 )
cellHeight [ 2 ] = arrow . off
default :
cellHeight [ 0 ] = Fr ( 1 )
cellHeight [ 2 ] = Fr ( 1 )
if arrow . off . Type != Auto && arrow . off . Value != 0 {
arrowView . Set ( MarginTop , arrow . off )
}
}
params [ CellHeight ] = cellHeight
}
return NewGridLayout ( session , params )
}
2024-11-25 23:13:29 +03:00
func ( popup * popupData ) layerCellWidth ( arrowLocation int , popupParams Params , session Session ) [ ] SizeUnit {
2022-08-07 18:59:56 +03:00
2024-11-25 23:13:29 +03:00
var columnCount int
switch arrowLocation {
case LeftArrow , RightArrow :
2022-08-07 18:59:56 +03:00
columnCount = 4
default :
2024-11-25 23:13:29 +03:00
columnCount = 3
2022-08-07 18:59:56 +03:00
}
cellWidth := make ( [ ] SizeUnit , columnCount )
switch hAlign , _ := enumProperty ( popupParams , HorizontalAlign , session , CenterAlign ) ; hAlign {
case LeftAlign :
cellWidth [ columnCount - 1 ] = Fr ( 1 )
case RightAlign :
cellWidth [ 0 ] = Fr ( 1 )
default :
cellWidth [ 0 ] = Fr ( 1 )
cellWidth [ columnCount - 1 ] = Fr ( 1 )
}
2024-11-25 23:13:29 +03:00
return cellWidth
}
func ( popup * popupData ) layerCellHeight ( arrowLocation int , popupParams Params , session Session ) [ ] SizeUnit {
var rowCount int
switch arrowLocation {
case TopArrow , BottomArrow :
rowCount = 4
default :
rowCount = 3
}
2022-08-07 18:59:56 +03:00
cellHeight := make ( [ ] SizeUnit , rowCount )
switch vAlign , _ := enumProperty ( popupParams , VerticalAlign , session , CenterAlign ) ; vAlign {
case LeftAlign :
cellHeight [ rowCount - 1 ] = Fr ( 1 )
case RightAlign :
cellHeight [ 0 ] = Fr ( 1 )
default :
cellHeight [ 0 ] = Fr ( 1 )
cellHeight [ rowCount - 1 ] = Fr ( 1 )
}
2024-11-25 23:13:29 +03:00
return cellHeight
}
func ( popup * popupData ) init ( view View , popupParams Params ) {
popup . contentView = view
popup . cancelable = false
session := view . Session ( )
popupRow := 1
popupColumn := 1
arrow := popupArrow {
row : 1 ,
column : 1 ,
align : CenterAlign ,
}
switch arrow . location , _ = enumProperty ( popupParams , Arrow , session , NoneArrow ) ; arrow . location {
case TopArrow :
popupRow = 2
case BottomArrow :
arrow . row = 2
case LeftArrow :
popupColumn = 2
case RightArrow :
arrow . column = 2
}
2022-08-07 18:59:56 +03:00
layerParams := Params {
Style : "ruiPopupLayer" ,
MaxWidth : Percent ( 100 ) ,
MaxHeight : Percent ( 100 ) ,
2024-11-25 23:13:29 +03:00
CellWidth : popup . layerCellWidth ( arrow . location , popupParams , session ) ,
CellHeight : popup . layerCellHeight ( arrow . location , popupParams , session ) ,
2022-08-07 18:59:56 +03:00
}
2022-07-31 15:37:26 +03:00
params := Params {
Style : "ruiPopup" ,
2024-11-25 23:13:29 +03:00
ID : "ruiPopup" ,
2022-08-07 18:59:56 +03:00
Row : popupRow ,
Column : popupColumn ,
2022-07-31 15:37:26 +03:00
MaxWidth : Percent ( 100 ) ,
MaxHeight : Percent ( 100 ) ,
CellVerticalAlign : StretchAlign ,
CellHorizontalAlign : StretchAlign ,
ClickEvent : func ( View ) { } ,
2024-12-03 10:25:55 +03:00
Shadow : NewShadowProperty ( Params {
2022-08-07 18:59:56 +03:00
SpreadRadius : Px ( 4 ) ,
Blur : Px ( 16 ) ,
ColorTag : "@ruiPopupShadow" ,
} ) ,
2022-07-08 13:16:42 +03:00
}
2022-08-07 18:59:56 +03:00
var closeButton View = nil
2024-11-25 23:13:29 +03:00
var title View = nil
2022-07-31 15:37:26 +03:00
outsideClose := false
2024-11-25 23:13:29 +03:00
popup . buttons = [ ] PopupButton { }
2022-07-31 15:37:26 +03:00
titleStyle := "ruiPopupTitle"
2024-11-25 23:13:29 +03:00
popup . showOpacity = 1.0
popup . showDuration = 1.0
popup . showTiming = "easy"
2021-11-17 12:32:37 +03:00
2022-07-31 15:37:26 +03:00
for tag , value := range popupParams {
if value != nil {
switch tag {
2022-08-07 18:59:56 +03:00
case VerticalAlign , HorizontalAlign , Arrow , Row , Column :
// Do nothing
case Margin :
layerParams [ Padding ] = value
case MarginLeft :
layerParams [ PaddingLeft ] = value
case MarginRight :
layerParams [ PaddingRight ] = value
case MarginTop :
layerParams [ PaddingTop ] = value
case MarginBottom :
layerParams [ PaddingBottom ] = value
2022-07-31 15:37:26 +03:00
case CloseButton :
2022-08-07 18:59:56 +03:00
closeButton = NewGridLayout ( session , Params {
Column : 1 ,
Height : "@ruiPopupTitleHeight" ,
Width : "@ruiPopupTitleHeight" ,
CellHorizontalAlign : CenterAlign ,
CellVerticalAlign : CenterAlign ,
TextSize : Px ( 20 ) ,
Content : "✕" ,
NotTranslate : true ,
2023-05-15 15:27:37 +03:00
ClickEvent : popup . cancel ,
2022-08-07 18:59:56 +03:00
} )
2023-05-15 15:27:37 +03:00
popup . cancelable = true
2021-11-17 12:32:37 +03:00
2022-07-31 15:37:26 +03:00
case OutsideClose :
outsideClose , _ = boolProperty ( popupParams , OutsideClose , session )
2023-05-15 15:27:37 +03:00
if outsideClose {
popup . cancelable = true
}
2022-07-31 15:37:26 +03:00
case Buttons :
switch value := value . ( type ) {
case PopupButton :
2024-11-25 23:13:29 +03:00
popup . buttons = [ ] PopupButton { value }
2021-11-17 12:32:37 +03:00
2022-07-31 15:37:26 +03:00
case [ ] PopupButton :
2024-11-25 23:13:29 +03:00
popup . buttons = value
2021-11-17 12:32:37 +03:00
}
2022-07-31 15:37:26 +03:00
case Title :
switch value := value . ( type ) {
case string :
title = NewTextView ( view . Session ( ) , Params { Text : value } )
case View :
title = value
default :
notCompatibleType ( Title , value )
2021-11-17 12:32:37 +03:00
}
2022-07-31 15:37:26 +03:00
case TitleStyle :
switch value := value . ( type ) {
case string :
titleStyle = value
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
default :
notCompatibleType ( TitleStyle , value )
}
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
case DismissEvent :
2024-11-18 17:20:25 +03:00
if listeners , ok := valueToNoArgEventListeners [ Popup ] ( value ) ; ok {
2022-07-31 15:37:26 +03:00
if listeners != nil {
popup . dismissListener = listeners
}
} else {
notCompatibleType ( tag , value )
}
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
case ArrowAlign :
switch text := value . ( type ) {
case string :
switch text {
case "top" :
value = "left"
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
case "bottom" :
value = "right"
}
}
2022-08-07 18:59:56 +03:00
arrow . align , _ = enumProperty ( popupParams , ArrowAlign , session , CenterAlign )
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
case ArrowSize :
2022-08-07 18:59:56 +03:00
arrow . size , _ = sizeProperty ( popupParams , ArrowSize , session )
2021-09-07 17:36:50 +03:00
2022-07-31 15:37:26 +03:00
case ArrowOffset :
2022-08-07 18:59:56 +03:00
arrow . off , _ = sizeProperty ( popupParams , ArrowOffset , session )
2021-09-07 17:36:50 +03:00
2024-11-25 23:13:29 +03:00
case ShowOpacity :
if opacity , _ := floatProperty ( popupParams , ShowOpacity , session , 1 ) ; opacity >= 0 && opacity < 1 {
popup . showOpacity = opacity
}
case ShowTransform :
if transform := valueToTransformProperty ( value ) ; transform != nil && ! transform . empty ( ) {
popup . showTransform = transform
}
case ShowDuration :
if duration , _ := floatProperty ( popupParams , ShowDuration , session , 1 ) ; duration > 0 {
popup . showDuration = duration
}
case ShowTiming :
if text , ok := value . ( string ) ; ok {
text , _ = session . resolveConstants ( text )
if isTimingFunctionValid ( text ) {
popup . showTiming = text
}
}
2021-09-07 17:36:50 +03:00
default :
2022-07-31 15:37:26 +03:00
params [ tag ] = value
2021-09-07 17:36:50 +03:00
}
}
}
2024-11-25 23:13:29 +03:00
popup . popupView = NewGridLayout ( view . Session ( ) , params )
2022-07-31 15:37:26 +03:00
2022-08-07 18:59:56 +03:00
var popupCellHeight [ ] SizeUnit
2021-09-07 17:36:50 +03:00
viewRow := 0
2022-08-07 18:59:56 +03:00
if title != nil || closeButton != nil {
titleContent := [ ] View { }
if title != nil {
titleContent = append ( titleContent , title )
}
if closeButton != nil {
titleContent = append ( titleContent , closeButton )
}
2024-11-25 23:13:29 +03:00
popup . popupView . Append ( NewGridLayout ( session , Params {
2021-09-07 17:36:50 +03:00
Row : 0 ,
Style : titleStyle ,
2022-08-07 18:59:56 +03:00
CellWidth : [ ] any { Fr ( 1 ) , AutoSize ( ) } ,
2021-09-07 17:36:50 +03:00
CellVerticalAlign : CenterAlign ,
PaddingLeft : Px ( 12 ) ,
2022-08-07 18:59:56 +03:00
Content : titleContent ,
} ) )
2021-09-07 17:36:50 +03:00
2022-08-07 18:59:56 +03:00
viewRow = 1
popupCellHeight = [ ] SizeUnit { AutoSize ( ) , Fr ( 1 ) }
2021-09-07 17:36:50 +03:00
} else {
2022-08-07 18:59:56 +03:00
popupCellHeight = [ ] SizeUnit { Fr ( 1 ) }
2021-09-07 17:36:50 +03:00
}
view . Set ( Row , viewRow )
2024-11-25 23:13:29 +03:00
popup . popupView . Append ( view )
2021-09-07 17:36:50 +03:00
2024-11-25 23:13:29 +03:00
if buttonCount := len ( popup . buttons ) ; buttonCount > 0 {
2022-07-08 13:16:42 +03:00
buttonsAlign , _ := enumProperty ( params , ButtonsAlign , session , RightAlign )
2022-08-07 18:59:56 +03:00
popupCellHeight = append ( popupCellHeight , AutoSize ( ) )
2021-11-17 12:32:37 +03:00
gap , _ := sizeConstant ( session , "ruiPopupButtonGap" )
2021-09-07 17:36:50 +03:00
cellWidth := [ ] SizeUnit { }
for i := 0 ; i < buttonCount ; i ++ {
cellWidth = append ( cellWidth , Fr ( 1 ) )
}
buttonsPanel := NewGridLayout ( session , Params {
CellWidth : cellWidth ,
} )
if gap . Type != Auto && gap . Value > 0 {
buttonsPanel . Set ( Gap , gap )
buttonsPanel . Set ( Margin , gap )
}
2024-11-25 23:13:29 +03:00
for i , button := range popup . buttons {
2023-05-15 15:27:37 +03:00
title := button . Title
if title == "" && button . Type == CancelButton {
title = "Cancel"
}
buttonView := NewButton ( session , Params {
Column : i ,
Content : title ,
} )
if button . OnClick != nil {
fn := button . OnClick
buttonView . Set ( ClickEvent , func ( ) {
fn ( popup )
} )
} else if button . Type == CancelButton {
buttonView . Set ( ClickEvent , popup . cancel )
}
if button . Type == DefaultButton {
buttonView . Set ( Style , "ruiDefaultButton" )
}
buttonsPanel . Append ( buttonView )
2021-09-07 17:36:50 +03:00
}
2024-11-25 23:13:29 +03:00
popup . popupView . Append ( NewGridLayout ( session , Params {
2021-09-07 17:36:50 +03:00
Row : viewRow + 1 ,
CellHorizontalAlign : buttonsAlign ,
Content : buttonsPanel ,
} ) )
}
2024-11-25 23:13:29 +03:00
popup . popupView . Set ( CellHeight , popupCellHeight )
2022-08-07 18:59:56 +03:00
if arrow . location != NoneArrow {
2024-11-25 23:13:29 +03:00
layerParams [ Content ] = [ ] View { popup . popupView , arrow . createView ( popup . popupView ) }
2022-08-07 18:59:56 +03:00
} else {
2024-11-25 23:13:29 +03:00
layerParams [ Content ] = [ ] View { popup . popupView }
2022-08-07 18:59:56 +03:00
}
popup . layerView = NewGridLayout ( session , layerParams )
2024-11-25 23:13:29 +03:00
if popup . showOpacity != 1 || popup . showTransform != nil {
2024-12-06 19:52:57 +03:00
animation := NewAnimationProperty ( Params {
2024-11-25 23:13:29 +03:00
Duration : popup . showDuration ,
TimingFunction : popup . showTiming ,
} )
if popup . showOpacity != 1 {
popup . popupView . Set ( Opacity , popup . showOpacity )
popup . popupView . SetTransition ( Opacity , animation )
}
if popup . showTransform != nil {
popup . popupView . Set ( Transform , popup . showTransform )
popup . popupView . SetTransition ( Transform , animation )
}
} else {
session . updateCSSProperty ( "ruiPopupLayer" , "transition" , "" )
}
2021-09-07 17:36:50 +03:00
if outsideClose {
2023-05-15 15:27:37 +03:00
popup . layerView . Set ( ClickEvent , popup . cancel )
2021-09-07 17:36:50 +03:00
}
}
2024-11-25 23:13:29 +03:00
func ( popup * popupData ) showAnimation ( ) {
if popup . showOpacity != 1 || popup . showTransform != nil {
htmlID := popup . popupView . htmlID ( )
session := popup . Session ( )
if popup . showOpacity != 1 {
session . updateCSSProperty ( htmlID , string ( Opacity ) , "1" )
}
if popup . showTransform != nil {
session . updateCSSProperty ( htmlID , string ( Transform ) , "" )
}
}
}
func ( popup * popupData ) dissmissAnimation ( listener func ( PropertyName ) ) bool {
if popup . showOpacity != 1 || popup . showTransform != nil {
session := popup . Session ( )
popup . popupView . Set ( TransitionEndEvent , listener )
popup . popupView . Set ( TransitionCancelEvent , listener )
htmlID := popup . popupView . htmlID ( )
if popup . showOpacity != 1 {
session . updateCSSProperty ( htmlID , string ( Opacity ) , fmt . Sprintf ( "%.2f" , popup . showOpacity ) )
}
if popup . showTransform != nil {
session . updateCSSProperty ( htmlID , string ( Transform ) , popup . showTransform . transformCSS ( session ) )
}
return true
}
return false
}
func ( popup * popupData ) View ( ) View {
return popup . contentView
2021-09-07 17:36:50 +03:00
}
func ( popup * popupData ) Session ( ) Session {
2024-11-25 23:13:29 +03:00
return popup . contentView . Session ( )
2021-09-07 17:36:50 +03:00
}
2023-05-15 15:27:37 +03:00
func ( popup * popupData ) cancel ( ) {
for _ , button := range popup . buttons {
if button . Type == CancelButton && button . OnClick != nil {
button . OnClick ( popup )
return
}
}
popup . Dismiss ( )
}
2021-09-07 17:36:50 +03:00
func ( popup * popupData ) Dismiss ( ) {
popup . Session ( ) . popupManager ( ) . dismissPopup ( popup )
}
func ( popup * popupData ) Show ( ) {
popup . Session ( ) . popupManager ( ) . showPopup ( popup )
}
func ( popup * popupData ) html ( buffer * strings . Builder ) {
2024-11-21 09:25:46 +03:00
viewHTML ( popup . layerView , buffer , "" )
2021-09-07 17:36:50 +03:00
}
func ( popup * popupData ) viewByHTMLID ( id string ) View {
return viewByHTMLID ( id , popup . layerView )
}
2022-07-08 13:16:42 +03:00
func ( popup * popupData ) onDismiss ( ) {
2024-11-25 23:13:29 +03:00
popup . Session ( ) . callFunc ( "removeView" , popup . layerView . htmlID ( ) )
2022-07-08 13:16:42 +03:00
for _ , listener := range popup . dismissListener {
listener ( popup )
}
}
2023-05-15 15:27:37 +03:00
func ( popup * popupData ) keyEvent ( event KeyEvent ) bool {
if ! event . AltKey && ! event . CtrlKey && ! event . ShiftKey && ! event . MetaKey {
2023-05-29 17:19:47 +03:00
switch event . Code {
case EnterKey :
2023-05-15 15:27:37 +03:00
for _ , button := range popup . buttons {
if button . Type == DefaultButton && button . OnClick != nil {
button . OnClick ( popup )
return true
}
}
2023-05-29 17:19:47 +03:00
case EscapeKey :
2023-05-15 15:27:37 +03:00
if popup . cancelable {
popup . Dismiss ( )
return true
}
}
}
return false
}
2021-09-07 17:36:50 +03:00
// NewPopup creates a new Popup
func NewPopup ( view View , param Params ) Popup {
if view == nil {
return nil
}
popup := new ( popupData )
popup . init ( view , param )
return popup
}
2022-07-08 13:16:42 +03:00
// ShowPopup creates a new Popup and shows it
func ShowPopup ( view View , param Params ) Popup {
popup := NewPopup ( view , param )
if popup != nil {
popup . Show ( )
}
return popup
}
2021-09-07 17:36:50 +03:00
func ( manager * popupManager ) updatePopupLayerInnerHTML ( session Session ) {
if manager . popups == nil {
manager . popups = [ ] Popup { }
2022-10-30 17:22:33 +03:00
session . updateInnerHTML ( "ruiPopupLayer" , "" )
2021-09-07 17:36:50 +03:00
return
}
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
2022-10-29 20:16:40 +03:00
for _ , popup := range manager . popups {
popup . html ( buffer )
2021-09-07 17:36:50 +03:00
}
2022-10-30 17:22:33 +03:00
session . updateInnerHTML ( "ruiPopupLayer" , buffer . String ( ) )
2021-09-07 17:36:50 +03:00
}
func ( manager * popupManager ) showPopup ( popup Popup ) {
if popup == nil {
return
}
session := popup . Session ( )
2024-08-20 20:01:26 +03:00
if len ( manager . popups ) == 0 {
2021-09-07 17:36:50 +03:00
manager . popups = [ ] Popup { popup }
} else {
manager . popups = append ( manager . popups , popup )
}
2021-11-21 18:30:46 +03:00
2022-11-02 20:10:19 +03:00
session . callFunc ( "blurCurrent" )
2021-09-07 17:36:50 +03:00
manager . updatePopupLayerInnerHTML ( session )
2023-04-25 17:20:47 +03:00
session . updateCSSProperty ( "ruiTooltipLayer" , "visibility" , "hidden" )
2023-04-25 17:33:08 +03:00
session . updateCSSProperty ( "ruiTooltipLayer" , "opacity" , "0" )
2022-10-30 17:22:33 +03:00
session . updateCSSProperty ( "ruiPopupLayer" , "visibility" , "visible" )
session . updateCSSProperty ( "ruiRoot" , "pointer-events" , "none" )
2024-11-25 23:13:29 +03:00
popup . showAnimation ( )
2021-09-07 17:36:50 +03:00
}
func ( manager * popupManager ) dismissPopup ( popup Popup ) {
if manager . popups == nil {
manager . popups = [ ] Popup { }
return
}
count := len ( manager . popups )
if count <= 0 || popup == nil {
return
}
2024-11-25 23:13:29 +03:00
index := - 1
for n , p := range manager . popups {
if p == popup {
index = n
break
2021-09-07 17:36:50 +03:00
}
2024-11-25 23:13:29 +03:00
}
if index < 0 {
2021-09-07 17:36:50 +03:00
return
}
2024-11-25 23:13:29 +03:00
session := popup . Session ( )
listener := func ( PropertyName ) {
if index == count - 1 {
if count == 1 {
manager . popups = [ ] Popup { }
session . updateCSSProperty ( "ruiRoot" , "pointer-events" , "auto" )
session . updateCSSProperty ( "ruiPopupLayer" , "visibility" , "hidden" )
2021-09-07 17:36:50 +03:00
} else {
2024-11-25 23:13:29 +03:00
manager . popups = manager . popups [ : count - 1 ]
2021-09-07 17:36:50 +03:00
}
2024-11-25 23:13:29 +03:00
} else if index == 0 {
manager . popups = manager . popups [ 1 : ]
} else {
manager . popups = append ( manager . popups [ : index ] , manager . popups [ index + 1 : ] ... )
2021-09-07 17:36:50 +03:00
}
2024-11-25 23:13:29 +03:00
popup . onDismiss ( )
}
if ! popup . dissmissAnimation ( listener ) {
listener ( "" )
2021-09-07 17:36:50 +03:00
}
}