2021-09-07 17:36:50 +03:00
package rui
import (
"fmt"
"strings"
)
2024-09-12 14:05:11 +03:00
// Constants which represent [StackLayout] animation type during pushing or popping views
2021-09-07 17:36:50 +03:00
const (
2024-11-24 09:52:43 +03:00
// PushTransform is the constant for "push-transform" property tag.
//
// Used by `StackLayout`.
// Specify start translation, scale and rotation over x, y and z axes as well as a distortion
// for an animated pushing of a child view.
//
// Supported types: `TransformProperty`, `string`.
//
// See `TransformProperty` description for more details.
//
// Conversion rules:
// `TransformProperty` - stored as is, no conversion performed.
// `string` - string representation of `Transform` interface. Example: "_{translate-x = 10px, scale-y = 1.1}".
PushTransform = "push-transform"
// PushDuration is the constant for "push-duration" property tag.
//
// Used by `StackLayout`.
// Sets the length of time in seconds that an push/pop animation takes to complete.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushDuration = "push-duration"
// PushTiming is the constant for "push-timing" property tag.
//
// Used by `StackLayout`.
// Set how an push/pop animation progresses through the duration of each cycle.
//
// Supported types: `string`.
//
// Values:
// "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.
PushTiming = "push-timing"
// MoveToFrontAnimation is the constant for "move-to-front-animation" property tag.
//
// Used by `StackLayout`.
// Specifies whether animation is used when calling the MoveToFront/MoveToFrontByID method of StackLayout interface.
//
// Supported types: `bool`, `int`, `string`.
//
// Values:
// `true` or `1` or "true", "yes", "on", "1" - animation is used (default value).
// `false` or `0` or "false", "no", "off", "0" - animation is not used.
MoveToFrontAnimation = "move-to-front-animation"
* Added "push-perspective", "push-rotate-x", "push-rotate-y", "push-rotate-z", "push-rotate", "push-skew-x", "push-skew-y", "push-scale-x", "push-scale-y", "push-scale-z", "push-translate-x", "push-translate-y", "push-translate-z" properties.
2024-11-25 13:03:08 +03:00
// PushPerspective is the constant for "push-perspective" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "perspective" property of StackLayout "push-transform" property:
// Distance between the z-plane and the user in order to give a 3D-positioned element some perspective. Each 3D element
// with z > 0 becomes larger, each 3D-element with z < 0 becomes smaller. The default value is 0 (no 3D effects).
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
PushPerspective PropertyName = "push-perspective"
// PushTranslateX is the constant for "push-translate-x" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "translate-x" property of StackLayout "push-transform" property:
// x-axis translation value of a 2D/3D translation.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
PushTranslateX PropertyName = "push-translate-x"
// PushTranslateY is the constant for "push-translate-y" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "translate-y" property of StackLayout "push-transform" property:
// y-axis translation value of a 2D/3D translation.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
PushTranslateY PropertyName = "push-translate-y"
// PushTranslateZ is the constant for "push-translate-z" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "translate-z" property of StackLayout "push-transform" property:
// z-axis translation value of a 3D translation.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
PushTranslateZ PropertyName = "push-translate-z"
// PushScaleX is the constant for "push-scale-x" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "scale-x" property of StackLayout "push-transform" property:
// x-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
// scale, more than 1 - to increase. The default value is 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushScaleX PropertyName = "push-scale-x"
// PushScaleY is the constant for "push-scale-y" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "scale-y" property of StackLayout "push-transform" property:
// y-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
// scale, more than 1 - to increase. The default value is 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushScaleY PropertyName = "push-scale-y"
// PushScaleZ is the constant for "push-scale-z" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "scale-z" property of StackLayout "push-transform" property:
// z-axis scaling value of a 3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
// scale, more than 1 - to increase. The default value is 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushScaleZ PropertyName = "push-scale-z"
// PushRotate is the constant for "push-rotate" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "rotate" property of StackLayout "push-transform" property:
// Angle of the view rotation. A positive angle denotes a clockwise rotation, a negative angle a counter-clockwise.
//
// Supported types: `AngleUnit`, `string`, `float`, `int`.
//
// Internal type is `AngleUnit`, other types will be converted to it during assignment.
// See `AngleUnit` description for more details.
//
// Conversion rules:
// `AngleUnit` - stored as is, no conversion performed.
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
PushRotate PropertyName = "push-rotate"
// PushRotateX is the constant for "push-rotate-x" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "rotate-x" property of StackLayout "push-transform" property:
// x-coordinate of the vector denoting the axis of rotation in range 0 to 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushRotateX PropertyName = "push-rotate-x"
// PushRotateY is the constant for "push-rotate-y" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "rotate-y" property of StackLayout "push-transform" property:
// y-coordinate of the vector denoting the axis of rotation in range 0 to 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushRotateY PropertyName = "push-rotate-y"
// PushRotateZ is the constant for "push-rotate-z" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "rotate-z" property of StackLayout "push-transform" property:
// z-coordinate of the vector denoting the axis of rotation in range 0 to 1.
//
// Supported types: `float`, `int`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
PushRotateZ PropertyName = "push-rotate-z"
// PushSkewX is the constant for "push-skew-x" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "skew-x" property of StackLayout "push-transform" property:
// Angle to use to distort the element along the abscissa. The default value is 0.
//
// Supported types: `AngleUnit`, `string`, `float`, `int`.
//
// Internal type is `AngleUnit`, other types will be converted to it during assignment.
// See `AngleUnit` description for more details.
//
// Conversion rules:
// `AngleUnit` - stored as is, no conversion performed.
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
PushSkewX PropertyName = "push-skew-x"
// PushSkewY is the constant for "push-skew-y" property tag.
//
// Used by `StackLayout`.
//
// Used to access the "skew-y" property of StackLayout "push-transform" property:
// Angle to use to distort the element along the ordinate. The default value is 0.
//
// Supported types: `AngleUnit`, `string`, `float`, `int`.
//
// Internal type is `AngleUnit`, other types will be converted to it during assignment.
// See `AngleUnit` description for more details.
//
// Conversion rules:
// `AngleUnit` - stored as is, no conversion performed.
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
PushSkewY PropertyName = "push-skew-y"
2021-09-07 17:36:50 +03:00
)
2024-09-12 14:05:11 +03:00
// StackLayout represents a StackLayout view
2021-09-07 17:36:50 +03:00
type StackLayout interface {
ViewsContainer
2024-04-23 19:34:36 +03:00
// Peek returns the current (visible) View. If StackLayout is empty then it returns nil.
2021-09-07 17:36:50 +03:00
Peek ( ) View
2024-04-23 19:34:36 +03:00
// RemovePeek removes the current View and returns it. If StackLayout is empty then it doesn't do anything and returns nil.
RemovePeek ( ) View
2024-11-24 22:14:35 +03:00
// MoveToFront makes the given View current.
// The second argument is a function called after the move to front animation ends.
// Returns true if successful, false otherwise.
MoveToFront ( view View , onShown ... func ( View ) ) bool
2024-04-23 19:34:36 +03:00
2024-11-24 22:14:35 +03:00
// MoveToFrontByID makes the View current by viewID.
// The second argument is a function called after the move to front animation ends.
// Returns true if successful, false otherwise.
MoveToFrontByID ( viewID string , onShown ... func ( View ) ) bool
2024-04-23 19:34:36 +03:00
// Push adds a new View to the container and makes it current.
// It is similar to Append, but the addition is done using an animation effect.
// The animation type is specified by the second argument and can take the following values:
// * DefaultAnimation (0) - Default animation. For the Push function it is EndToStartAnimation, for Pop - StartToEndAnimation;
// * StartToEndAnimation (1) - Animation from beginning to end. The beginning and the end are determined by the direction of the text output;
// * EndToStartAnimation (2) - End-to-Beginning animation;
// * TopDownAnimation (3) - Top-down animation;
// * BottomUpAnimation (4) - Bottom up animation.
2024-11-24 22:14:35 +03:00
// The second argument `onPushFinished` is the function to be called when the animation ends.
Push ( view View , onPushFinished ... func ( ) )
2024-04-23 19:34:36 +03:00
// Pop removes the current View from the container using animation.
2024-11-24 22:14:35 +03:00
// The argument `onPopFinished` is the function to be called when the animation ends.
2024-04-23 19:34:36 +03:00
// The function will return false if the StackLayout is empty and true if the current item has been removed.
2024-11-24 22:14:35 +03:00
Pop ( onPopFinished ... func ( View ) ) bool
2024-11-24 09:52:43 +03:00
}
type pushFinished struct {
peekID string
2024-11-24 22:14:35 +03:00
listener [ ] func ( )
2024-11-24 09:52:43 +03:00
}
type popFinished struct {
view View
2024-11-24 22:14:35 +03:00
listener [ ] func ( View )
2021-09-07 17:36:50 +03:00
}
type stackLayoutData struct {
viewsContainerData
2024-11-24 09:52:43 +03:00
onPushFinished map [ string ] pushFinished
onPopFinished map [ string ] popFinished
2024-11-24 22:14:35 +03:00
onMoveFinished map [ string ] popFinished
2021-09-07 17:36:50 +03:00
}
// NewStackLayout create new StackLayout object and return it
func NewStackLayout ( session Session , params Params ) StackLayout {
view := new ( stackLayoutData )
2022-09-01 11:04:50 +03:00
view . init ( session )
2021-09-07 17:36:50 +03:00
setInitParams ( view , params )
return view
}
func newStackLayout ( session Session ) View {
2024-11-13 12:56:39 +03:00
//return NewStackLayout(session, nil)
return new ( stackLayoutData )
2021-09-07 17:36:50 +03:00
}
// Init initialize fields of ViewsContainer by default values
2022-09-01 11:04:50 +03:00
func ( layout * stackLayoutData ) init ( session Session ) {
layout . viewsContainerData . init ( session )
2021-09-07 17:36:50 +03:00
layout . tag = "StackLayout"
layout . systemClass = "ruiStackLayout"
2024-11-24 09:52:43 +03:00
layout . onPushFinished = map [ string ] pushFinished { }
layout . onPopFinished = map [ string ] popFinished { }
2024-11-24 22:14:35 +03:00
layout . onMoveFinished = map [ string ] popFinished { }
2024-11-13 12:56:39 +03:00
layout . set = layout . setFunc
layout . remove = layout . removeFunc
* Added "push-perspective", "push-rotate-x", "push-rotate-y", "push-rotate-z", "push-rotate", "push-skew-x", "push-skew-y", "push-scale-x", "push-scale-y", "push-scale-z", "push-translate-x", "push-translate-y", "push-translate-z" properties.
2024-11-25 13:03:08 +03:00
layout . changed = layout . propertyChanged
2024-11-24 09:52:43 +03:00
layout . setRaw ( TransitionEndEvent , [ ] func ( View , PropertyName ) { layout . transitionFinished } )
if session . TextDirection ( ) == RightToLeftDirection {
layout . setRaw ( PushTransform , NewTransformProperty ( Params { TranslateX : Percent ( - 100 ) } ) )
} else {
layout . setRaw ( PushTransform , NewTransformProperty ( Params { TranslateX : Percent ( 100 ) } ) )
}
2022-05-22 12:54:02 +03:00
}
2024-11-24 09:52:43 +03:00
func ( layout * stackLayoutData ) transitionFinished ( view View , tag PropertyName ) {
if tags := strings . Split ( string ( tag ) , "-" ) ; len ( tags ) >= 2 {
session := layout . Session ( )
viewID := tags [ 1 ]
switch tags [ 0 ] {
case "push" :
if finished , ok := layout . onPushFinished [ viewID ] ; ok {
if finished . peekID != "" {
pageID := finished . peekID + "page"
session . startUpdateScript ( pageID )
session . updateCSSProperty ( pageID , "visibility" , "hidden" )
session . updateCSSProperty ( pageID , "transition" , "" )
session . updateCSSProperty ( pageID , "transform" , "" )
session . removeProperty ( pageID , "ontransitionend" )
session . removeProperty ( pageID , "ontransitioncancel" )
session . finishUpdateScript ( pageID )
}
pageID := viewID + "page"
session . startUpdateScript ( pageID )
session . updateCSSProperty ( pageID , "z-index" , "auto" )
session . updateCSSProperty ( pageID , "transition" , "" )
session . removeProperty ( pageID , "ontransitionend" )
session . removeProperty ( pageID , "ontransitioncancel" )
session . finishUpdateScript ( pageID )
2024-11-24 22:14:35 +03:00
for _ , listener := range finished . listener {
if listener != nil {
listener ( )
}
2024-11-24 09:52:43 +03:00
}
delete ( layout . onPushFinished , viewID )
layout . contentChanged ( )
}
case "pop" :
if finished , ok := layout . onPopFinished [ viewID ] ; ok {
2024-11-24 18:23:24 +03:00
session . callFunc ( "removeView" , viewID + "page" )
2024-11-24 22:14:35 +03:00
for _ , listener := range finished . listener {
if listener != nil {
listener ( finished . view )
}
2024-11-24 09:52:43 +03:00
}
delete ( layout . onPopFinished , viewID )
if count := len ( layout . views ) ; count > 0 {
peekID := layout . views [ count - 1 ] . htmlID ( ) + "page"
session . startUpdateScript ( peekID )
session . removeProperty ( peekID , "ontransitionend" )
session . removeProperty ( peekID , "ontransitioncancel" )
session . finishUpdateScript ( peekID )
}
2021-09-07 17:36:50 +03:00
}
2021-10-04 17:58:17 +03:00
2024-11-24 09:52:43 +03:00
case "move" :
if count := len ( layout . views ) ; count > 1 {
pageID := layout . views [ count - 2 ] . htmlID ( ) + "page"
session . startUpdateScript ( pageID )
session . updateCSSProperty ( pageID , "visibility" , "hidden" )
session . updateCSSProperty ( pageID , "transition" , "" )
session . updateCSSProperty ( pageID , "transform" , "" )
session . finishUpdateScript ( pageID )
}
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
pageID := viewID + "page"
session . startUpdateScript ( pageID )
session . updateCSSProperty ( pageID , "z-index" , "auto" )
session . updateCSSProperty ( pageID , "transition" , "" )
session . removeProperty ( pageID , "ontransitionend" )
session . removeProperty ( pageID , "ontransitioncancel" )
session . finishUpdateScript ( pageID )
layout . contentChanged ( )
2024-11-24 22:14:35 +03:00
if finished , ok := layout . onMoveFinished [ viewID ] ; ok {
for _ , listener := range finished . listener {
if listener != nil {
listener ( finished . view )
}
}
delete ( layout . onMoveFinished , viewID )
}
2021-09-07 17:36:50 +03:00
}
}
}
2024-11-18 17:20:25 +03:00
func ( layout * stackLayoutData ) setFunc ( tag PropertyName , value any ) [ ] PropertyName {
2021-11-20 14:22:00 +03:00
switch tag {
case TransitionEndEvent :
2024-11-24 09:52:43 +03:00
// TODO
2024-11-22 16:36:08 +03:00
listeners , ok := valueToOneArgEventListeners [ View , PropertyName ] ( value )
2022-07-27 20:31:57 +03:00
if ok && listeners != nil {
2024-11-24 09:52:43 +03:00
listeners = append ( listeners , layout . transitionFinished )
2024-11-18 17:20:25 +03:00
layout . setRaw ( TransitionEndEvent , listeners )
2024-11-13 12:56:39 +03:00
return [ ] PropertyName { tag }
2021-10-04 17:58:17 +03:00
}
2024-11-13 12:56:39 +03:00
return nil
2021-11-20 14:22:00 +03:00
2024-11-24 09:52:43 +03:00
case PushTiming :
if text , ok := value . ( string ) ; ok {
layout . setRaw ( tag , text )
return [ ] PropertyName { tag }
2024-11-13 12:56:39 +03:00
}
2021-10-04 17:58:17 +03:00
}
2024-11-18 17:20:25 +03:00
return layout . viewsContainerData . setFunc ( tag , value )
2021-10-04 17:58:17 +03:00
}
* Added "push-perspective", "push-rotate-x", "push-rotate-y", "push-rotate-z", "push-rotate", "push-skew-x", "push-skew-y", "push-scale-x", "push-scale-y", "push-scale-z", "push-translate-x", "push-translate-y", "push-translate-z" properties.
2024-11-25 13:03:08 +03:00
func ( layout * stackLayoutData ) propertyChanged ( tag PropertyName ) {
switch tag {
case PushTransform , PushTiming , PushDuration , MoveToFrontAnimation ,
PushPerspective , PushRotateX , PushRotateY , PushRotateZ , PushRotate , PushSkewX , PushSkewY ,
PushScaleX , PushScaleY , PushScaleZ , PushTranslateX , PushTranslateY , PushTranslateZ :
// do nothing
default :
layout . viewsContainerData . propertyChanged ( tag )
}
}
2024-11-18 17:20:25 +03:00
func ( layout * stackLayoutData ) removeFunc ( tag PropertyName ) [ ] PropertyName {
2021-11-20 14:22:00 +03:00
switch tag {
case TransitionEndEvent :
2024-11-24 09:52:43 +03:00
layout . setRaw ( TransitionEndEvent , [ ] func ( View , PropertyName ) { layout . transitionFinished } )
2024-11-13 12:56:39 +03:00
return [ ] PropertyName { tag }
2021-10-04 17:58:17 +03:00
}
2024-11-18 17:20:25 +03:00
return layout . viewsContainerData . removeFunc ( tag )
2021-10-04 17:58:17 +03:00
}
2021-09-07 17:36:50 +03:00
func ( layout * stackLayoutData ) Peek ( ) View {
2024-11-24 09:52:43 +03:00
if count := len ( layout . views ) ; count > 0 {
return layout . views [ count - 1 ]
2021-09-07 17:36:50 +03:00
}
return nil
}
2024-11-24 22:14:35 +03:00
func ( layout * stackLayoutData ) MoveToFront ( view View , onShown ... func ( View ) ) bool {
2024-11-24 09:52:43 +03:00
if view == nil {
ErrorLog ( ` MoveToFront(nil) forbidden ` )
return false
}
2021-09-07 17:36:50 +03:00
htmlID := view . htmlID ( )
2024-11-24 09:52:43 +03:00
switch count := len ( layout . views ) ; count {
case 0 :
// do nothing
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
case 1 :
if layout . views [ 0 ] . htmlID ( ) == htmlID {
2021-09-07 17:36:50 +03:00
return true
}
2024-11-24 09:52:43 +03:00
default :
for i , view := range layout . views {
if view . htmlID ( ) == htmlID {
2024-11-24 22:14:35 +03:00
layout . moveToFrontByIndex ( i , onShown )
2024-11-24 09:52:43 +03:00
return true
}
}
2021-09-07 17:36:50 +03:00
}
2024-11-24 09:52:43 +03:00
ErrorLog ( ` MoveToFront() fail. Subview not found. ` )
2021-09-07 17:36:50 +03:00
return false
}
2024-11-24 22:14:35 +03:00
func ( layout * stackLayoutData ) MoveToFrontByID ( viewID string , onShown ... func ( View ) ) bool {
2024-11-24 09:52:43 +03:00
switch count := len ( layout . views ) ; count {
case 0 :
// do nothing
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
case 1 :
if layout . views [ 0 ] . ID ( ) == viewID {
2021-09-07 17:36:50 +03:00
return true
}
2024-11-24 09:52:43 +03:00
default :
for i , view := range layout . views {
if view . ID ( ) == viewID {
2024-11-24 22:14:35 +03:00
layout . moveToFrontByIndex ( i , onShown )
2024-11-24 09:52:43 +03:00
return true
}
}
2021-09-07 17:36:50 +03:00
}
2024-11-24 09:52:43 +03:00
ErrorLogF ( ` MoveToFront("%s") fail. Subview with "%s" not found. ` , viewID , viewID )
2021-09-07 17:36:50 +03:00
return false
}
2024-11-24 22:14:35 +03:00
func ( layout * stackLayoutData ) moveToFrontByIndex ( index int , onShow [ ] func ( View ) ) {
2024-11-24 09:52:43 +03:00
count := len ( layout . views )
if index == count - 1 {
return
2021-09-07 17:36:50 +03:00
}
2024-11-24 09:52:43 +03:00
view := layout . views [ index ]
peekID := layout . views [ count - 1 ] . htmlID ( )
if index == 0 {
layout . views = append ( layout . views [ 1 : ] , view )
2021-09-07 17:36:50 +03:00
} else {
2024-11-24 09:52:43 +03:00
layout . views = append ( append ( layout . views [ : index ] , layout . views [ index + 1 : ] ... ) , view )
2021-09-07 17:36:50 +03:00
}
2024-11-24 09:52:43 +03:00
session := layout . Session ( )
pageID := view . htmlID ( ) + "page"
peekPageID := peekID + "page"
animated := IsMoveToFrontAnimation ( layout )
var transform TransformProperty = nil
if animated {
transform = GetPushTransform ( layout )
}
if transform == nil {
session . updateCSSProperty ( peekPageID , "visibility" , "hidden" )
session . updateCSSProperty ( pageID , "visibility" , "visible" )
layout . contentChanged ( )
2024-11-24 22:14:35 +03:00
for _ , listener := range onShow {
if listener != nil {
listener ( view )
}
}
2024-11-24 09:52:43 +03:00
return
2021-11-20 14:22:00 +03:00
}
2024-11-24 22:14:35 +03:00
layout . onMoveFinished [ view . htmlID ( ) ] = popFinished {
view : view ,
listener : onShow ,
}
2024-11-24 09:52:43 +03:00
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
buffer . WriteString ( ` stackTransitionEndEvent(' ` )
buffer . WriteString ( layout . htmlID ( ) )
buffer . WriteString ( ` ', 'move- ` )
buffer . WriteString ( view . htmlID ( ) )
buffer . WriteString ( ` ', event) ` )
listener := buffer . String ( )
transformCSS := transformMirror ( transform , session ) . transformCSS ( session )
transitionCSS := layout . pushTransitionCSS ( )
session . updateCSSProperty ( peekPageID , "transition" , transitionCSS )
session . startUpdateScript ( pageID )
session . updateProperty ( pageID , "ontransitionend" , listener )
session . updateProperty ( pageID , "ontransitioncancel" , listener )
session . updateCSSProperty ( pageID , "transform" , transformCSS )
session . updateCSSProperty ( pageID , "z-index" , "100" )
session . updateCSSProperty ( pageID , "visibility" , "visible" )
session . finishUpdateScript ( pageID )
session . updateCSSProperty ( pageID , "transition" , transitionCSS )
session . updateCSSProperty ( pageID , "transform" , "" )
session . updateCSSProperty ( peekPageID , "transform" , transformCSS )
}
func ( layout * stackLayoutData ) contentChanged ( ) {
if listener , ok := layout . changeListener [ Content ] ; ok {
listener ( layout , Content )
2021-09-07 17:36:50 +03:00
}
}
2024-04-23 19:34:36 +03:00
func ( layout * stackLayoutData ) RemovePeek ( ) View {
return layout . RemoveView ( len ( layout . views ) - 1 )
}
2024-11-24 09:52:43 +03:00
func ( layout * stackLayoutData ) pushTransitionCSS ( ) string {
return fmt . Sprintf ( "transform %.2fs %s" , GetPushDuration ( layout ) , GetPushTiming ( layout ) )
}
func transformMirror ( transform TransformProperty , session Session ) TransformProperty {
result := NewTransformProperty ( nil )
for _ , tag := range [ ] PropertyName { Perspective , RotateX , RotateY , RotateZ , ScaleX , ScaleY , ScaleZ , TranslateZ } {
if value := transform . getRaw ( tag ) ; value != nil {
result . Set ( tag , value )
}
}
for _ , tag := range [ ] PropertyName { Rotate , SkewX , SkewY } {
if angle , ok := angleProperty ( transform , tag , session ) ; ok {
angle . Value = - angle . Value
result . Set ( tag , angle )
}
}
for _ , tag := range [ ] PropertyName { TranslateX , TranslateY } {
if size , ok := sizeProperty ( transform , tag , session ) ; ok {
size . Value = - size . Value
result . Set ( tag , size )
}
}
return result
}
2024-11-24 18:23:24 +03:00
// Append appends a view to the end of the list of a view children
func ( layout * stackLayoutData ) Append ( view View ) {
if view == nil {
ErrorLog ( "StackLayout.Append(nil) is forbidden" )
return
}
stackID := layout . htmlID ( )
view . setParentID ( stackID )
count := len ( layout . views )
if count == 0 {
layout . views = [ ] View { view }
} else {
layout . views = append ( layout . views , view )
}
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
buffer . WriteString ( ` <div id=" ` )
buffer . WriteString ( view . htmlID ( ) )
buffer . WriteString ( ` page" class="ruiStackPageLayout"> ` )
viewHTML ( view , buffer , "" )
buffer . WriteString ( ` </div> ` )
session := layout . Session ( )
if count > 0 {
session . updateCSSProperty ( layout . views [ count - 1 ] . htmlID ( ) + "page" , "visibility" , "hidden" )
}
session . appendToInnerHTML ( stackID , buffer . String ( ) )
if listener , ok := layout . changeListener [ Content ] ; ok {
listener ( layout , Content )
}
}
// Insert inserts a view to the "index" position in the list of a view children
func ( layout * stackLayoutData ) Insert ( view View , index int ) {
if view == nil {
ErrorLog ( "StackLayout.Insert(nil, ...) is forbidden" )
return
}
if layout . views == nil || index < 0 || index >= len ( layout . views ) {
layout . Append ( view )
return
}
stackID := layout . htmlID ( )
view . setParentID ( stackID )
if index > 0 {
layout . views = append ( layout . views [ : index ] , append ( [ ] View { view } , layout . views [ index : ] ... ) ... )
} else {
layout . views = append ( [ ] View { view } , layout . views ... )
}
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
buffer . WriteString ( ` <div id=" ` )
buffer . WriteString ( view . htmlID ( ) )
buffer . WriteString ( ` page" class="ruiStackPageLayout" style="visibility: hidden;"> ` )
viewHTML ( view , buffer , "" )
buffer . WriteString ( ` </div> ` )
session := layout . Session ( )
session . appendToInnerHTML ( stackID , buffer . String ( ) )
if listener , ok := layout . changeListener [ Content ] ; ok {
listener ( layout , Content )
}
}
// Remove removes view from list and return it
func ( layout * stackLayoutData ) RemoveView ( index int ) View {
if layout . views == nil {
layout . views = [ ] View { }
return nil
}
count := len ( layout . views )
if index < 0 || index >= count {
return nil
}
session := layout . Session ( )
view := layout . views [ index ]
view . setParentID ( "" )
if count == 1 {
layout . views = [ ] View { }
} else if index == 0 {
layout . views = layout . views [ 1 : ]
} else if index == count - 1 {
layout . views = layout . views [ : index ]
session . updateCSSProperty ( layout . views [ count - 2 ] . htmlID ( ) + "page" , "visibility" , "visible" )
} else {
layout . views = append ( layout . views [ : index ] , layout . views [ index + 1 : ] ... )
}
layout . Session ( ) . callFunc ( "removeView" , view . htmlID ( ) + "page" )
if listener , ok := layout . changeListener [ Content ] ; ok {
listener ( layout , Content )
}
return view
}
2024-11-24 22:14:35 +03:00
func ( layout * stackLayoutData ) Push ( view View , onPushFinished ... func ( ) ) {
2021-09-07 17:36:50 +03:00
if view == nil {
ErrorLog ( "StackLayout.Push(nil, ....) is forbidden" )
return
}
2024-11-24 09:52:43 +03:00
transform := GetPushTransform ( layout )
if transform == nil {
layout . Append ( view )
2024-11-24 22:14:35 +03:00
for _ , listener := range onPushFinished {
if listener != nil {
listener ( )
}
2024-11-24 09:52:43 +03:00
}
return
}
prevPeek := ""
finished := pushFinished {
listener : onPushFinished ,
}
if count := len ( layout . views ) ; count > 0 {
finished . peekID = layout . views [ count - 1 ] . htmlID ( )
prevPeek = finished . peekID + "page"
}
htmlID := view . htmlID ( )
layout . onPushFinished [ htmlID ] = finished
view . setParentID ( layout . htmlID ( ) )
layout . views = append ( layout . views , view )
2021-09-07 17:36:50 +03:00
session := layout . Session ( )
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
buffer . WriteString ( ` <div id=" ` )
buffer . WriteString ( htmlID )
2024-11-24 09:52:43 +03:00
buffer . WriteString ( ` page" class="ruiStackPageLayout" ontransitionend="stackTransitionEndEvent(' ` )
buffer . WriteString ( layout . htmlID ( ) )
buffer . WriteString ( ` ', 'push- ` )
2021-09-07 17:36:50 +03:00
buffer . WriteString ( htmlID )
2024-11-24 09:52:43 +03:00
buffer . WriteString ( ` ', event)" style="z-index: 100; transform: ` )
buffer . WriteString ( transform . transformCSS ( layout . session ) )
buffer . WriteRune ( ';' )
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
transitionCSS := layout . pushTransitionCSS ( )
buffer . WriteString ( " transition: " )
buffer . WriteString ( transitionCSS )
buffer . WriteString ( ` ;"> ` )
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
viewHTML ( view , buffer , "" )
2021-09-07 17:36:50 +03:00
buffer . WriteString ( ` </div> ` )
2024-11-24 09:52:43 +03:00
session . appendToInnerHTML ( layout . htmlID ( ) , buffer . String ( ) )
2024-11-13 12:56:39 +03:00
2024-11-24 09:52:43 +03:00
if prevPeek != "" {
mirror := transformMirror ( transform , session )
layout . session . updateCSSProperty ( prevPeek , "transition" , transitionCSS )
layout . session . updateCSSProperty ( prevPeek , "transform" , mirror . transformCSS ( session ) )
2024-11-13 12:56:39 +03:00
}
2024-11-24 09:52:43 +03:00
layout . session . updateCSSProperty ( htmlID + "page" , "transform" , "" )
2021-09-07 17:36:50 +03:00
}
2024-11-24 22:14:35 +03:00
func ( layout * stackLayoutData ) Pop ( onPopFinished ... func ( View ) ) bool {
2021-11-20 14:22:00 +03:00
count := len ( layout . views )
2024-11-24 09:52:43 +03:00
if count == 0 {
2021-09-07 17:36:50 +03:00
ErrorLog ( "StackLayout is empty" )
return false
}
2024-11-24 09:52:43 +03:00
transform := GetPushTransform ( layout )
if transform == nil {
if view := layout . RemovePeek ( ) ; view != nil {
2024-11-24 22:14:35 +03:00
for _ , listener := range onPopFinished {
if listener != nil {
listener ( view )
}
2024-11-24 09:52:43 +03:00
}
return true
}
return false
}
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
peek := count - 1
view := layout . views [ peek ]
view . setParentID ( "" )
2021-09-07 17:36:50 +03:00
2024-11-24 09:52:43 +03:00
layout . views = layout . views [ : peek ]
layout . contentChanged ( )
layout . onPopFinished [ view . htmlID ( ) ] = popFinished {
view : view ,
listener : onPopFinished ,
}
htmlID := view . htmlID ( )
2021-09-07 17:36:50 +03:00
session := layout . Session ( )
buffer := allocStringBuilder ( )
defer freeStringBuilder ( buffer )
2024-11-24 09:52:43 +03:00
buffer . WriteString ( ` stackTransitionEndEvent(' ` )
buffer . WriteString ( layout . htmlID ( ) )
buffer . WriteString ( ` ', 'pop- ` )
2021-09-07 17:36:50 +03:00
buffer . WriteString ( htmlID )
2024-11-24 09:52:43 +03:00
buffer . WriteString ( ` ', event) ` )
listener := buffer . String ( )
pageID := htmlID + "page"
transitionCSS := layout . pushTransitionCSS ( )
session . startUpdateScript ( pageID )
session . updateProperty ( pageID , "ontransitionend" , listener )
session . updateProperty ( pageID , "ontransitioncancel" , listener )
session . updateCSSProperty ( pageID , "z-index" , "100" )
session . updateCSSProperty ( pageID , "transition" , transitionCSS )
session . finishUpdateScript ( pageID )
peek --
if peek >= 0 {
peekID := layout . views [ peek ] . htmlID ( ) + "page"
session . updateCSSProperty ( peekID , "transition" , "" )
session . startUpdateScript ( peekID )
session . updateCSSProperty ( peekID , "transform" , transformMirror ( transform , session ) . transformCSS ( session ) )
session . updateCSSProperty ( peekID , "visibility" , "visible" )
session . finishUpdateScript ( peekID )
session . updateCSSProperty ( peekID , "transition" , transitionCSS )
session . updateCSSProperty ( peekID , "transform" , "" )
2021-09-07 17:36:50 +03:00
}
2024-11-24 09:52:43 +03:00
session . updateCSSProperty ( pageID , "transform" , transform . transformCSS ( session ) )
2021-09-07 17:36:50 +03:00
return true
}
func ( layout * stackLayoutData ) htmlSubviews ( self View , buffer * strings . Builder ) {
2024-11-24 09:52:43 +03:00
if count := len ( layout . views ) ; count > 0 {
peek := count - 1
2021-09-07 17:36:50 +03:00
for i , view := range layout . views {
buffer . WriteString ( ` <div id=" ` )
2024-11-24 09:52:43 +03:00
buffer . WriteString ( view . htmlID ( ) )
2021-09-07 17:36:50 +03:00
buffer . WriteString ( ` page ` )
buffer . WriteString ( ` " class="ruiStackPageLayout" ` )
if i != peek {
buffer . WriteString ( ` style="visibility: hidden;" ` )
}
buffer . WriteString ( ` > ` )
2024-11-21 09:25:46 +03:00
viewHTML ( view , buffer , "" )
2021-09-07 17:36:50 +03:00
buffer . WriteString ( ` </div> ` )
}
}
}
2024-11-24 09:52:43 +03:00
// IsMoveToFrontAnimation returns "true" if an animation is used when calling the MoveToFront/MoveToFrontByID method of StackLayout interface.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func IsMoveToFrontAnimation ( view View , subviewID ... string ) bool {
2024-11-24 16:43:31 +03:00
if view = getSubview ( view , subviewID ) ; view != nil {
2024-11-24 09:52:43 +03:00
if value , ok := boolProperty ( view , MoveToFrontAnimation , view . Session ( ) ) ; ok {
return value
}
if value := valueFromStyle ( view , MoveToFrontAnimation ) ; value != nil {
if b , ok := valueToBool ( value , view . Session ( ) ) ; ok {
return b
}
}
}
return true
}
// GetPushDuration returns the length of time in seconds that an push/pop StackLayout animation takes to complete.
// If the second argument (subviewID) is not specified or it is "" then a width of the first argument (view) is returned
func GetPushDuration ( view View , subviewID ... string ) float64 {
return floatStyledProperty ( view , subviewID , PushDuration , 1 )
}
// GetPushTiming returns the function which sets how an push/pop animation progresses.
// If the second argument (subviewID) is not specified or it is "" then a width of the first argument (view) is returned
func GetPushTiming ( view View , subviewID ... string ) string {
result := stringStyledProperty ( view , subviewID , PushTiming , false )
if isTimingFunctionValid ( result ) {
return result
}
return "easy"
}
// GetTransform returns the start transform (translation, scale and rotation over x, y and z axes as well as a distortion)
// for an animated pushing of a child view.
// The default value is nil (no transform).
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPushTransform ( view View , subviewID ... string ) TransformProperty {
return transformStyledProperty ( view , subviewID , PushTransform )
}