From d79f986fc480456627a5c0346c48c57bf92b1968 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Thu, 16 Nov 2023 12:39:52 +0700 Subject: [PATCH 01/13] Add slice action to slider --- ui/cryptomaterial/slide_action.go | 193 ++++++++++++++++++++++++++++++ ui/cryptomaterial/slider.go | 167 +++++++++++++++++++++++++- 2 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 ui/cryptomaterial/slide_action.go diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go new file mode 100644 index 000000000..83bbee68e --- /dev/null +++ b/ui/cryptomaterial/slide_action.go @@ -0,0 +1,193 @@ +package cryptomaterial + +import ( + "fmt" + "image" + "time" + + "gioui.org/f32" + "gioui.org/gesture" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op" +) + +const defaultDuration = 300 * time.Millisecond + +type DragDirection int + +const ( + SlideLeft DragDirection = iota + SlideRight +) + +type Dragged func(dragDirection DragDirection) + +type SliceShow struct { + Duration time.Duration + + push int + + next *op.Ops + + nextCall op.CallOp + lastCall op.CallOp + + t0 time.Time + offset float32 + + // animation state + dragging bool + dragStarted f32.Point + dragOffset int + + drag gesture.Drag + Draged Dragged + canPush bool + + IsShowCarousel bool +} + +func (t *Theme) SliceShow() *SliceShow { + return &SliceShow{ + canPush: true, + IsShowCarousel: true, + } +} + +// PushLeft pushes the existing widget to the left. +func (s *SliceShow) PushLeft() { s.push = 1 } + +// PushRight pushes the existing widget to the right. +func (s *SliceShow) PushRight() { s.push = -1 } + +func (s *SliceShow) Layout(gtx C, w layout.Widget) D { + for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { + switch event.Type { + case pointer.Press: + fmt.Println("----drag.Events---------Press----") + s.dragStarted = event.Position + s.dragOffset = 0 + s.dragging = true + case pointer.Drag: + newOffset := int(s.dragStarted.X - event.Position.X) + fmt.Println("----drag.Events---------Drag----", newOffset) + if newOffset > 100 { + if s.canPush && s.Draged != nil { + s.canPush = false + s.Draged(SlideRight) + } + } else if newOffset < -100 { + if s.canPush && s.Draged != nil { + s.canPush = false + s.Draged(SlideLeft) + } + } + s.dragOffset = newOffset + case pointer.Release: + fmt.Println("----drag.Events---------Release----") + fallthrough + case pointer.Cancel: + fmt.Println("----drag.Events---------Cancel----") + s.dragging = false + s.canPush = true + } + } + + if s.push != 0 { + s.next = nil + s.lastCall = s.nextCall + s.offset = float32(s.push) + s.t0 = gtx.Now + s.push = 0 + } + + var delta time.Duration + if !s.t0.IsZero() { + now := gtx.Now + delta = now.Sub(s.t0) + s.t0 = now + } + + if s.offset != 0 { + duration := s.Duration + if duration == 0 { + duration = defaultDuration + } + movement := float32(delta.Seconds()) / float32(duration.Seconds()) + if s.offset < 0 { + s.offset += movement + if s.offset >= 0 { + s.offset = 0 + } + } else { + s.offset -= movement + if s.offset <= 0 { + s.offset = 0 + } + } + + op.InvalidateOp{}.Add(gtx.Ops) + } + + var dims layout.Dimensions + { + if s.next == nil { + s.next = new(op.Ops) + } + gtx := gtx + gtx.Ops = s.next + gtx.Ops.Reset() + m := op.Record(gtx.Ops) + dims = w(gtx) + s.nextCall = m.Stop() + } + + s.drag.Add(gtx.Ops) + + if s.offset == 0 { + s.nextCall.Add(gtx.Ops) + return dims + } + + offset := smooth(s.offset) + + if s.offset > 0 { + defer op.Offset(image.Point{ + X: int(float32(dims.Size.X) * (offset - 1)), + }).Push(gtx.Ops).Pop() + s.lastCall.Add(gtx.Ops) + + defer op.Offset(image.Point{ + X: dims.Size.X, + }).Push(gtx.Ops).Pop() + s.nextCall.Add(gtx.Ops) + } else { + defer op.Offset(image.Point{ + X: int(float32(dims.Size.X) * (offset + 1)), + }).Push(gtx.Ops).Pop() + s.lastCall.Add(gtx.Ops) + + defer op.Offset(image.Point{ + X: -dims.Size.X, + }).Push(gtx.Ops).Pop() + s.nextCall.Add(gtx.Ops) + } + return dims +} + +// smooth handles -1 to 1 with ease-in-out cubic easing func. +// func smooth(t float32) float32 { +// if t < 0 { +// return -easeInOutCubic(-t) +// } +// return easeInOutCubic(t) +// } + +// // easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. +// func easeInOutCubic(t float32) float32 { +// if t < 0.5 { +// return 4 * t * t * t +// } +// return (t-1)*(2*t-2)*(2*t-2) + 1 +// } diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 660f45f05..5d1f5d467 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -3,9 +3,15 @@ package cryptomaterial import ( + "image" "image/color" + "time" + "gioui.org/f32" + "gioui.org/gesture" + "gioui.org/io/pointer" "gioui.org/layout" + "gioui.org/op" "github.com/crypto-power/cryptopower/ui/values" ) @@ -24,6 +30,25 @@ type Slider struct { ButtonBackgroundColor color.NRGBA IndicatorBackgroundColor color.NRGBA SelectedIndicatorColor color.NRGBA // this is a full color no opacity + + Duration time.Duration + push int + next *op.Ops + nextCall op.CallOp + lastCall op.CallOp + t0 time.Time + offset float32 + + // animation state + dragging bool + dragStarted f32.Point + dragOffset int + + drag gesture.Drag + Draged Dragged + canPush bool + + IsShowCarousel bool } var m4 = values.MarginPadding4 @@ -38,6 +63,7 @@ func (t *Theme) Slider() *Slider { ButtonBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), IndicatorBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), SelectedIndicatorColor: t.Color.White, + canPush: true, } sl.card = sl.t.Card() @@ -52,6 +78,129 @@ func (s *Slider) GetSelectedIndex() int { } func (s *Slider) Layout(gtx C, items []layout.Widget) D { + for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { + switch event.Type { + case pointer.Press: + s.dragStarted = event.Position + s.dragOffset = 0 + s.dragging = true + case pointer.Drag: + newOffset := int(s.dragStarted.X - event.Position.X) + if newOffset > 100 && s.canPush { + s.handleActionEvent(true) + s.canPush = false + } else if newOffset < -100 && s.canPush { + s.handleActionEvent(false) + s.canPush = false + } + s.dragOffset = newOffset + case pointer.Release: + fallthrough + case pointer.Cancel: + s.dragging = false + s.canPush = true + } + } + + if s.push != 0 { + s.next = nil + s.lastCall = s.nextCall + s.offset = float32(s.push) + s.t0 = gtx.Now + s.push = 0 + } + + var delta time.Duration + if !s.t0.IsZero() { + now := gtx.Now + delta = now.Sub(s.t0) + s.t0 = now + } + + if s.offset != 0 { + duration := s.Duration + if duration == 0 { + duration = defaultDuration + } + movement := float32(delta.Seconds()) / float32(duration.Seconds()) + if s.offset < 0 { + s.offset += movement + if s.offset >= 0 { + s.offset = 0 + } + } else { + s.offset -= movement + if s.offset <= 0 { + s.offset = 0 + } + } + + op.InvalidateOp{}.Add(gtx.Ops) + } + + var dims layout.Dimensions + { + if s.next == nil { + s.next = new(op.Ops) + } + gtx := gtx + gtx.Ops = s.next + gtx.Ops.Reset() + m := op.Record(gtx.Ops) + dims = s.layout(gtx, items) + s.nextCall = m.Stop() + } + + s.drag.Add(gtx.Ops) + + if s.offset == 0 { + s.nextCall.Add(gtx.Ops) + return dims + } + + offset := smooth(s.offset) + + if s.offset > 0 { + defer op.Offset(image.Point{ + X: int(float32(dims.Size.X) * (offset - 1)), + }).Push(gtx.Ops).Pop() + s.lastCall.Add(gtx.Ops) + + defer op.Offset(image.Point{ + X: dims.Size.X, + }).Push(gtx.Ops).Pop() + s.nextCall.Add(gtx.Ops) + } else { + defer op.Offset(image.Point{ + X: int(float32(dims.Size.X) * (offset + 1)), + }).Push(gtx.Ops).Pop() + s.lastCall.Add(gtx.Ops) + + defer op.Offset(image.Point{ + X: -dims.Size.X, + }).Push(gtx.Ops).Pop() + s.nextCall.Add(gtx.Ops) + } + return dims +} + +// smooth handles -1 to 1 with ease-in-out cubic easing func. +func smooth(t float32) float32 { + if t < 0 { + return -easeInOutCubic(-t) + } + return easeInOutCubic(t) +} + +// easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. +func easeInOutCubic(t float32) float32 { + if t < 0.5 { + return 4 * t * t * t + } + return (t-1)*(2*t-2)*(2*t-2) + 1 +} + +func (s *Slider) layout(gtx C, items []layout.Widget) D { // set slider items once since layout is drawn multiple times per sec. if !s.isSliderItemsSet { s.items = items @@ -147,20 +296,30 @@ func (s *Slider) RefreshItems() { } func (s *Slider) handleClickEvent() { - l := len(s.items) - 1 // index starts at 0 if s.nextButton.Clicked() { + s.handleActionEvent(true) + } + + if s.prevButton.Clicked() { + s.handleActionEvent(false) + } +} + +func (s *Slider) handleActionEvent(isNext bool) { + l := len(s.items) - 1 // index starts at 0 + if isNext { if s.selected == l { s.selected = 0 } else { s.selected++ } - } - - if s.prevButton.Clicked() { + s.push = 1 + } else { if s.selected == 0 { s.selected = l } else { s.selected-- } + s.push = -1 } } From 97dd2118575db735847ffea19b98a6c6cffde562 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Thu, 16 Nov 2023 21:30:17 +0700 Subject: [PATCH 02/13] add slide action, update logic on slide action, implement slide action to slider, update logic click to indicator on slider --- ui/cryptomaterial/slide_action.go | 97 ++++++------ ui/cryptomaterial/slider.go | 237 +++++++++--------------------- 2 files changed, 108 insertions(+), 226 deletions(-) diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 83bbee68e..2705737c9 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -1,7 +1,6 @@ package cryptomaterial import ( - "fmt" "image" "time" @@ -23,13 +22,10 @@ const ( type Dragged func(dragDirection DragDirection) -type SliceShow struct { - Duration time.Duration - - push int - - next *op.Ops - +type SliceAction struct { + duration time.Duration + push int + next *op.Ops nextCall op.CallOp lastCall op.CallOp @@ -37,63 +33,56 @@ type SliceShow struct { offset float32 // animation state - dragging bool dragStarted f32.Point dragOffset int - - drag gesture.Drag - Draged Dragged - canPush bool - - IsShowCarousel bool -} - -func (t *Theme) SliceShow() *SliceShow { - return &SliceShow{ - canPush: true, - IsShowCarousel: true, - } + drag gesture.Drag + draged Dragged + isPushing bool } // PushLeft pushes the existing widget to the left. -func (s *SliceShow) PushLeft() { s.push = 1 } +func (s *SliceAction) PushLeft() { s.push = 1 } // PushRight pushes the existing widget to the right. -func (s *SliceShow) PushRight() { s.push = -1 } +func (s *SliceAction) PushRight() { s.push = -1 } -func (s *SliceShow) Layout(gtx C, w layout.Widget) D { +func (s *SliceAction) Draged(drag Dragged) { + s.draged = drag +} + +func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { switch event.Type { case pointer.Press: - fmt.Println("----drag.Events---------Press----") s.dragStarted = event.Position s.dragOffset = 0 - s.dragging = true case pointer.Drag: newOffset := int(s.dragStarted.X - event.Position.X) - fmt.Println("----drag.Events---------Drag----", newOffset) if newOffset > 100 { - if s.canPush && s.Draged != nil { - s.canPush = false - s.Draged(SlideRight) + if !s.isPushing && s.draged != nil { + s.isPushing = true + s.draged(SlideLeft) } } else if newOffset < -100 { - if s.canPush && s.Draged != nil { - s.canPush = false - s.Draged(SlideLeft) + if !s.isPushing && s.draged != nil { + s.isPushing = true + s.draged(SlideRight) } } s.dragOffset = newOffset case pointer.Release: - fmt.Println("----drag.Events---------Release----") fallthrough case pointer.Cancel: - fmt.Println("----drag.Events---------Cancel----") - s.dragging = false - s.canPush = true + s.isPushing = false } } + s.drag.Add(gtx.Ops) + + return w(gtx) +} + +func (s *SliceAction) TransformLayout(gtx C, w layout.Widget) D { if s.push != 0 { s.next = nil s.lastCall = s.nextCall @@ -110,7 +99,7 @@ func (s *SliceShow) Layout(gtx C, w layout.Widget) D { } if s.offset != 0 { - duration := s.Duration + duration := s.duration if duration == 0 { duration = defaultDuration } @@ -143,8 +132,6 @@ func (s *SliceShow) Layout(gtx C, w layout.Widget) D { s.nextCall = m.Stop() } - s.drag.Add(gtx.Ops) - if s.offset == 0 { s.nextCall.Add(gtx.Ops) return dims @@ -177,17 +164,17 @@ func (s *SliceShow) Layout(gtx C, w layout.Widget) D { } // smooth handles -1 to 1 with ease-in-out cubic easing func. -// func smooth(t float32) float32 { -// if t < 0 { -// return -easeInOutCubic(-t) -// } -// return easeInOutCubic(t) -// } - -// // easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. -// func easeInOutCubic(t float32) float32 { -// if t < 0.5 { -// return 4 * t * t * t -// } -// return (t-1)*(2*t-2)*(2*t-2) + 1 -// } +func smooth(t float32) float32 { + if t < 0 { + return -easeInOutCubic(-t) + } + return easeInOutCubic(t) +} + +// easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. +func easeInOutCubic(t float32) float32 { + if t < 0.5 { + return 4 * t * t * t + } + return (t-1)*(2*t-2)*(2*t-2) + 1 +} diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 5d1f5d467..8223fcee1 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -3,26 +3,25 @@ package cryptomaterial import ( - "image" "image/color" - "time" - "gioui.org/f32" - "gioui.org/gesture" - "gioui.org/io/pointer" "gioui.org/layout" - "gioui.org/op" "github.com/crypto-power/cryptopower/ui/values" ) +type sliderItem struct { + widgetItem layout.Widget + button *Clickable +} + type Slider struct { t *Theme nextButton *Clickable prevButton *Clickable card Card - items []layout.Widget + slideItems []*sliderItem selected int isSliderItemsSet bool @@ -30,45 +29,31 @@ type Slider struct { ButtonBackgroundColor color.NRGBA IndicatorBackgroundColor color.NRGBA SelectedIndicatorColor color.NRGBA // this is a full color no opacity - - Duration time.Duration - push int - next *op.Ops - nextCall op.CallOp - lastCall op.CallOp - t0 time.Time - offset float32 - - // animation state - dragging bool - dragStarted f32.Point - dragOffset int - - drag gesture.Drag - Draged Dragged - canPush bool - - IsShowCarousel bool + sliceAction SliceAction } var m4 = values.MarginPadding4 func (t *Theme) Slider() *Slider { sl := &Slider{ - t: t, - items: make([]layout.Widget, 0), + t: t, + slideItems: make([]*sliderItem, 0), nextButton: t.NewClickable(false), prevButton: t.NewClickable(false), ButtonBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), IndicatorBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), SelectedIndicatorColor: t.Color.White, - canPush: true, } sl.card = sl.t.Card() sl.card.Radius = Radius(8) + sl.sliceAction.Draged(func(dragDirection DragDirection) { + isNext := dragDirection == SlideLeft + sl.handleActionEvent(isNext) + }) + return sl } @@ -77,158 +62,51 @@ func (s *Slider) GetSelectedIndex() int { return s.selected } -func (s *Slider) Layout(gtx C, items []layout.Widget) D { - for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { - switch event.Type { - case pointer.Press: - s.dragStarted = event.Position - s.dragOffset = 0 - s.dragging = true - case pointer.Drag: - newOffset := int(s.dragStarted.X - event.Position.X) - if newOffset > 100 && s.canPush { - s.handleActionEvent(true) - s.canPush = false - } else if newOffset < -100 && s.canPush { - s.handleActionEvent(false) - s.canPush = false - } - s.dragOffset = newOffset - case pointer.Release: - fallthrough - case pointer.Cancel: - s.dragging = false - s.canPush = true - } - } - - if s.push != 0 { - s.next = nil - s.lastCall = s.nextCall - s.offset = float32(s.push) - s.t0 = gtx.Now - s.push = 0 - } - - var delta time.Duration - if !s.t0.IsZero() { - now := gtx.Now - delta = now.Sub(s.t0) - s.t0 = now - } - - if s.offset != 0 { - duration := s.Duration - if duration == 0 { - duration = defaultDuration - } - movement := float32(delta.Seconds()) / float32(duration.Seconds()) - if s.offset < 0 { - s.offset += movement - if s.offset >= 0 { - s.offset = 0 - } - } else { - s.offset -= movement - if s.offset <= 0 { - s.offset = 0 - } - } - - op.InvalidateOp{}.Add(gtx.Ops) - } - - var dims layout.Dimensions - { - if s.next == nil { - s.next = new(op.Ops) - } - gtx := gtx - gtx.Ops = s.next - gtx.Ops.Reset() - m := op.Record(gtx.Ops) - dims = s.layout(gtx, items) - s.nextCall = m.Stop() - } - - s.drag.Add(gtx.Ops) - - if s.offset == 0 { - s.nextCall.Add(gtx.Ops) - return dims - } - - offset := smooth(s.offset) - - if s.offset > 0 { - defer op.Offset(image.Point{ - X: int(float32(dims.Size.X) * (offset - 1)), - }).Push(gtx.Ops).Pop() - s.lastCall.Add(gtx.Ops) - - defer op.Offset(image.Point{ - X: dims.Size.X, - }).Push(gtx.Ops).Pop() - s.nextCall.Add(gtx.Ops) - } else { - defer op.Offset(image.Point{ - X: int(float32(dims.Size.X) * (offset + 1)), - }).Push(gtx.Ops).Pop() - s.lastCall.Add(gtx.Ops) - - defer op.Offset(image.Point{ - X: -dims.Size.X, - }).Push(gtx.Ops).Pop() - s.nextCall.Add(gtx.Ops) +func (s *Slider) getSliceItems(items []layout.Widget) []*sliderItem { + slideItems := make([]*sliderItem, 0) + for _, item := range items { + slideItems = append(slideItems, &sliderItem{ + widgetItem: item, + button: s.t.NewClickable(false), + }) } - return dims -} -// smooth handles -1 to 1 with ease-in-out cubic easing func. -func smooth(t float32) float32 { - if t < 0 { - return -easeInOutCubic(-t) - } - return easeInOutCubic(t) + return slideItems } -// easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. -func easeInOutCubic(t float32) float32 { - if t < 0.5 { - return 4 * t * t * t - } - return (t-1)*(2*t-2)*(2*t-2) + 1 -} - -func (s *Slider) layout(gtx C, items []layout.Widget) D { +func (s *Slider) Layout(gtx C, items []layout.Widget) D { // set slider items once since layout is drawn multiple times per sec. if !s.isSliderItemsSet { - s.items = items + s.slideItems = s.getSliceItems(items) s.isSliderItemsSet = true } - if len(s.items) == 0 { + if len(s.slideItems) == 0 { return D{} } s.handleClickEvent() - gtx.Constraints.Max = s.items[s.selected](gtx).Size + gtx.Constraints.Max = s.slideItems[s.selected].widgetItem(gtx).Size return layout.Stack{Alignment: layout.S}.Layout(gtx, - layout.Expanded(s.items[s.selected]), + layout.Expanded(func(gtx C) D { + return s.sliceAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) + }), layout.Stacked(func(gtx C) D { - return layout.Inset{ - Right: values.MarginPadding15, - Left: values.MarginPadding15, - Bottom: values.MarginPadding10, - }.Layout(gtx, func(gtx C) D { - return layout.Flex{ - Axis: layout.Horizontal, - }.Layout(gtx, - layout.Rigid(s.selectedItemIndicatorLayout), - layout.Flexed(1, func(gtx C) D { - return layout.E.Layout(gtx, s.buttonLayout) - }), - ) + return s.sliceAction.DragLayout(gtx, func(gtx C) D { + return layout.Inset{ + Right: values.MarginPadding15, + Left: values.MarginPadding15, + Bottom: values.MarginPadding10, + }.Layout(gtx, func(gtx C) D { + return layout.Flex{ + Axis: layout.Horizontal, + }.Layout(gtx, + layout.Rigid(s.selectedItemIndicatorLayout), + layout.Flexed(1, func(gtx C) D { + return layout.E.Layout(gtx, s.buttonLayout) + }), + ) + }) }) }), ) @@ -268,7 +146,7 @@ func (s *Slider) selectedItemIndicatorLayout(gtx C) D { Left: m4, }.Layout(gtx, func(gtx C) D { list := &layout.List{Axis: layout.Horizontal} - return list.Layout(gtx, len(s.items), func(gtx C, i int) D { + return list.Layout(gtx, len(s.slideItems), func(gtx C, i int) D { ic := NewIcon(s.t.Icons.ImageBrightness1) ic.Color = values.TransparentColor(values.TransparentBlack, 0.2) if i == s.selected { @@ -280,7 +158,9 @@ func (s *Slider) selectedItemIndicatorLayout(gtx C) D { Right: m4, Left: m4, }.Layout(gtx, func(gtx C) D { - return ic.Layout(gtx, values.MarginPadding12) + return s.slideItems[i].button.Layout(gtx, func(gtx C) D { + return ic.Layout(gtx, values.MarginPadding12) + }) }) }) }) @@ -303,23 +183,38 @@ func (s *Slider) handleClickEvent() { if s.prevButton.Clicked() { s.handleActionEvent(false) } + + for i, item := range s.slideItems { + if item.button.Clicked() { + if i == s.selected { + continue + } + lastSelected := s.selected + s.selected = i + if lastSelected < i { + s.sliceAction.PushLeft() + } else { + s.sliceAction.PushRight() + } + } + } } func (s *Slider) handleActionEvent(isNext bool) { - l := len(s.items) - 1 // index starts at 0 + l := len(s.slideItems) - 1 // index starts at 0 if isNext { if s.selected == l { s.selected = 0 } else { s.selected++ } - s.push = 1 + s.sliceAction.PushLeft() } else { if s.selected == 0 { s.selected = l } else { s.selected-- } - s.push = -1 + s.sliceAction.PushRight() } } From f8b2c54a0441bd935ab1552f2a0c2546164a804e Mon Sep 17 00:00:00 2001 From: Justin Do Date: Mon, 20 Nov 2023 00:49:09 +0700 Subject: [PATCH 03/13] implement slide action to segmented control, update layout for main page, trade page --- ui/cryptomaterial/segmented_control.go | 81 ++++++++++++++++++++++++-- ui/cryptomaterial/slide_action.go | 23 +++++--- ui/cryptomaterial/slider.go | 6 +- ui/cryptomaterial/util.go | 20 +++++++ ui/page/root/main_page.go | 23 +++----- ui/page/root/trade_page.go | 18 +----- ui/values/dimensions.go | 1 + 7 files changed, 125 insertions(+), 47 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index 566852122..f80922e62 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -9,6 +9,13 @@ import ( "github.com/crypto-power/cryptopower/ui/values" ) +type SegmentType int + +const ( + Group SegmentType = iota + Split +) + type SegmentedControl struct { theme *Theme list *ClickableList @@ -21,22 +28,66 @@ type SegmentedControl struct { changed bool mu sync.Mutex + + isEnableSwipe bool + sliceAction SliceAction + segmentType SegmentType } -func (t *Theme) SegmentedControl(segmentTitles []string) *SegmentedControl { +func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType) *SegmentedControl { list := t.NewClickableList(layout.Horizontal) list.IsHoverable = false - return &SegmentedControl{ + sc := &SegmentedControl{ list: list, theme: t, segmentTitles: segmentTitles, leftNavBtn: t.NewClickable(false), rightNavBtn: t.NewClickable(false), + isEnableSwipe: true, + segmentType: segmentType, } + + sc.sliceAction.Draged(func(dragDirection SwipeDirection) { + isNext := dragDirection == SwipeLeft + sc.handleActionEvent(isNext) + }) + + return sc +} + +func (sc *SegmentedControl) SetEnableSwipe(enable bool) { + sc.isEnableSwipe = enable } -func (sc *SegmentedControl) Layout(gtx C) D { +func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { + return UniformPadding(gtx, func(gtx C) D { + return layout.Flex{ + Axis: layout.Vertical, + Alignment: layout.Middle, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + if sc.segmentType == Group { + return sc.GroupTileLayout(gtx) + } else { + return sc.splitTileLayout(gtx) + } + }), + layout.Rigid(func(gtx C) D { + return layout.Inset{Top: values.MarginPadding16}.Layout(gtx, func(gtx C) D { + if sc.isEnableSwipe { + return sc.sliceAction.DragLayout(gtx, func(gtx C) D { + return sc.sliceAction.TransformLayout(gtx, body) + }, true) + } + return body(gtx) + }) + }), + ) + }) +} + +func (sc *SegmentedControl) GroupTileLayout(gtx C) D { sc.handleEvents() return LinearLayout{ @@ -71,10 +122,10 @@ func (sc *SegmentedControl) Layout(gtx C) D { ) } -func (sc *SegmentedControl) TransparentLayout(gtx C) D { +func (sc *SegmentedControl) splitTileLayout(gtx C) D { sc.handleEvents() return LinearLayout{ - Width: gtx.Dp(values.MarginPadding600), + Width: gtx.Dp(values.MarginPadding700), Height: WrapContent, Orientation: layout.Horizontal, Alignment: layout.Middle, @@ -176,3 +227,23 @@ func (sc *SegmentedControl) SetSelectedSegment(segment string) { } } } + +func (s *SegmentedControl) handleActionEvent(isNext bool) { + l := len(s.segmentTitles) - 1 // index starts at 0 + if isNext { + if s.selectedIndex == l { + s.selectedIndex = 0 + } else { + s.selectedIndex++ + } + s.sliceAction.PushLeft() + } else { + if s.selectedIndex == 0 { + s.selectedIndex = l + } else { + s.selectedIndex-- + } + s.sliceAction.PushRight() + } + s.changed = true +} diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 2705737c9..655d96a2e 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -9,18 +9,19 @@ import ( "gioui.org/io/pointer" "gioui.org/layout" "gioui.org/op" + "gioui.org/op/clip" ) const defaultDuration = 300 * time.Millisecond -type DragDirection int +type SwipeDirection int const ( - SlideLeft DragDirection = iota - SlideRight + SwipeLeft SwipeDirection = iota + SwipeRight ) -type Dragged func(dragDirection DragDirection) +type Dragged func(dragDirection SwipeDirection) type SliceAction struct { duration time.Duration @@ -50,7 +51,7 @@ func (s *SliceAction) Draged(drag Dragged) { s.draged = drag } -func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { +func (s *SliceAction) DragLayout(gtx C, w layout.Widget, isWrapContent bool) D { for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { switch event.Type { case pointer.Press: @@ -61,12 +62,12 @@ func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { if newOffset > 100 { if !s.isPushing && s.draged != nil { s.isPushing = true - s.draged(SlideLeft) + s.draged(SwipeLeft) } } else if newOffset < -100 { if !s.isPushing && s.draged != nil { s.isPushing = true - s.draged(SlideRight) + s.draged(SwipeRight) } } s.dragOffset = newOffset @@ -77,7 +78,13 @@ func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { } } - s.drag.Add(gtx.Ops) + if isWrapContent { + area := clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops) + s.drag.Add(gtx.Ops) + defer area.Pop() + } else { + s.drag.Add(gtx.Ops) + } return w(gtx) } diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 8223fcee1..219862734 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -49,8 +49,8 @@ func (t *Theme) Slider() *Slider { sl.card = sl.t.Card() sl.card.Radius = Radius(8) - sl.sliceAction.Draged(func(dragDirection DragDirection) { - isNext := dragDirection == SlideLeft + sl.sliceAction.Draged(func(dragDirection SwipeDirection) { + isNext := dragDirection == SwipeLeft sl.handleActionEvent(isNext) }) @@ -107,7 +107,7 @@ func (s *Slider) Layout(gtx C, items []layout.Widget) D { }), ) }) - }) + }, false) }), ) } diff --git a/ui/cryptomaterial/util.go b/ui/cryptomaterial/util.go index 5432aa536..690805ade 100644 --- a/ui/cryptomaterial/util.go +++ b/ui/cryptomaterial/util.go @@ -14,7 +14,9 @@ import ( "gioui.org/op" "gioui.org/op/clip" "gioui.org/op/paint" + "gioui.org/unit" "gioui.org/widget" + "github.com/crypto-power/cryptopower/ui/values" ) func drawInk(gtx layout.Context, c widget.Press, highlightColor color.NRGBA) { @@ -136,3 +138,21 @@ func AnyKeyWithOptionalModifier(modifier key.Modifiers, keys ...string) key.Set keysWithModifier := fmt.Sprintf("(%s)-[%s]", modifier, keysCombined) return key.Set(keysWithModifier) } + +func UniformPadding(gtx layout.Context, body layout.Widget) layout.Dimensions { + width := gtx.Constraints.Max.X + + padding := values.MarginPadding24 + + if (width - 2*gtx.Dp(padding)) > gtx.Dp(values.AppWidth) { + paddingValue := float32(width-gtx.Dp(values.AppWidth)) / 4 + padding = unit.Dp(paddingValue) + } + + return layout.Inset{ + Top: values.MarginPadding24, + Right: padding, + Bottom: values.MarginPadding24, + Left: padding, + }.Layout(gtx, body) +} diff --git a/ui/page/root/main_page.go b/ui/page/root/main_page.go index c98d1690f..a3ab2d63f 100644 --- a/ui/page/root/main_page.go +++ b/ui/page/root/main_page.go @@ -148,7 +148,7 @@ func (mp *MainPage) initTabOptions() { commonTabs = append(commonTabs[:4], append(dcrSpecificTabs, commonTabs[4:]...)...) } - mp.pageNavigationTab = mp.Theme.SegmentedControl(commonTabs) + mp.pageNavigationTab = mp.Theme.SegmentedControl(commonTabs, cryptomaterial.Split) } func (mp *MainPage) isGovernanceAPIAllowed() bool { @@ -352,15 +352,10 @@ func (mp *MainPage) layoutDesktop(gtx C) D { }.Layout(gtx, layout.Rigid(mp.LayoutTopBar), layout.Rigid(func(gtx C) D { - return cryptomaterial.LinearLayout{ - Width: gtx.Dp(values.MarginPadding550), - Height: cryptomaterial.WrapContent, - Orientation: layout.Vertical, - Alignment: layout.Middle, - Margin: layout.Inset{Top: values.MarginPadding28}, - }.Layout(gtx, - layout.Rigid(mp.pageTabLayout), - layout.Rigid(func(gtx C) D { + return layout.Inset{ + Bottom: values.MarginPadding16, + }.Layout(gtx, func(gtx C) D { + return mp.pageNavigationTab.Layout(gtx, func(gtx C) D { if mp.CurrentPage() == nil { return D{} } @@ -376,8 +371,8 @@ func (mp *MainPage) layoutDesktop(gtx C) D { default: return mp.CurrentPage().Layout(gtx) } - }), - ) + }) + }) }), ) }), @@ -484,10 +479,6 @@ func (mp *MainPage) LayoutTopBar(gtx C) D { ) } -func (mp *MainPage) pageTabLayout(gtx C) D { - return layout.Inset{Bottom: values.MarginPadding16}.Layout(gtx, mp.pageNavigationTab.TransparentLayout) -} - func (mp *MainPage) LayoutUSDBalance(gtx C) D { if !mp.usdExchangeSet { return D{} diff --git a/ui/page/root/trade_page.go b/ui/page/root/trade_page.go index ce70b7eea..6de7f39e2 100644 --- a/ui/page/root/trade_page.go +++ b/ui/page/root/trade_page.go @@ -49,7 +49,7 @@ func NewTradePage(l *load.Load) *TradePage { }, } - pg.tab = l.Theme.SegmentedControl(tabTitles) + pg.tab = l.Theme.SegmentedControl(tabTitles, cryptomaterial.Group) rad := cryptomaterial.Radius(14) pg.exchangeBtn = l.Theme.NewClickable(false) @@ -122,19 +122,7 @@ func (pg *TradePage) Layout(gtx C) D { } func (pg *TradePage) layoutDesktop(gtx C) D { - return components.UniformPadding(gtx, func(gtx C) D { - return layout.Flex{ - Axis: layout.Vertical, - Alignment: layout.Middle, - }.Layout(gtx, - layout.Rigid(pg.sectionNavTab), - layout.Flexed(1, func(gtx C) D { - return layout.Inset{Top: values.MarginPadding16}.Layout(gtx, func(gtx C) D { - return pg.CurrentPage().Layout(gtx) - }) - }), - ) - }) + return pg.tab.Layout(gtx, pg.CurrentPage().Layout) } func (pg *TradePage) layoutMobile(gtx C) D { @@ -154,5 +142,5 @@ func (pg *TradePage) layoutMobile(gtx C) D { } func (pg *TradePage) sectionNavTab(gtx C) D { - return pg.tab.Layout(gtx) + return pg.tab.GroupTileLayout(gtx) } diff --git a/ui/values/dimensions.go b/ui/values/dimensions.go index 277a372d5..5f8ee10ec 100644 --- a/ui/values/dimensions.go +++ b/ui/values/dimensions.go @@ -92,6 +92,7 @@ var ( MarginPadding500 = unit.Dp(500) MarginPadding550 = unit.Dp(550) MarginPadding600 = unit.Dp(600) + MarginPadding700 = unit.Dp(700) TextSize10 = unit.Sp(10) TextSize12 = unit.Sp(12) From cacefb19a751db0a6a79f2ed8fb24239e3ad6ed1 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Mon, 20 Nov 2023 15:54:22 +0700 Subject: [PATCH 04/13] fix lint --- ui/cryptomaterial/segmented_control.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index f80922e62..52abe731c 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -69,9 +69,8 @@ func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { layout.Rigid(func(gtx C) D { if sc.segmentType == Group { return sc.GroupTileLayout(gtx) - } else { - return sc.splitTileLayout(gtx) } + return sc.splitTileLayout(gtx) }), layout.Rigid(func(gtx C) D { return layout.Inset{Top: values.MarginPadding16}.Layout(gtx, func(gtx C) D { @@ -228,22 +227,22 @@ func (sc *SegmentedControl) SetSelectedSegment(segment string) { } } -func (s *SegmentedControl) handleActionEvent(isNext bool) { - l := len(s.segmentTitles) - 1 // index starts at 0 +func (sc *SegmentedControl) handleActionEvent(isNext bool) { + l := len(sc.segmentTitles) - 1 // index starts at 0 if isNext { - if s.selectedIndex == l { - s.selectedIndex = 0 + if sc.selectedIndex == l { + sc.selectedIndex = 0 } else { - s.selectedIndex++ + sc.selectedIndex++ } - s.sliceAction.PushLeft() + sc.sliceAction.PushLeft() } else { - if s.selectedIndex == 0 { - s.selectedIndex = l + if sc.selectedIndex == 0 { + sc.selectedIndex = l } else { - s.selectedIndex-- + sc.selectedIndex-- } - s.sliceAction.PushRight() + sc.sliceAction.PushRight() } - s.changed = true + sc.changed = true } From 3cc5a8da1da33c08541c3657af6bd3f3bb418a33 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Wed, 22 Nov 2023 22:19:57 +0700 Subject: [PATCH 05/13] clean optimize code --- ui/cryptomaterial/segmented_control.go | 24 ++++++++++---------- ui/cryptomaterial/slider.go | 1 + ui/page/components/components.go | 18 --------------- ui/page/components/restore_page.go | 4 ++-- ui/page/exchange/order_details_page.go | 2 +- ui/page/exchange/order_history_page.go | 2 +- ui/page/governance/governance_page.go | 4 ++-- ui/page/governance/proposal_details_page.go | 2 +- ui/page/privacy/account_mixer_page.go | 2 +- ui/page/privacy/manual_mixer_setup_page.go | 2 +- ui/page/privacy/setup_mixer_accounts_page.go | 2 +- ui/page/privacy/setup_privacy_page.go | 2 +- ui/page/root/overview_page.go | 4 ++-- ui/page/seedbackup/backup_instructions.go | 2 +- ui/page/seedbackup/backup_success.go | 3 +-- ui/page/send/manual_coin_selection.go | 3 +-- ui/page/settings/btc_account_details_page.go | 2 +- ui/page/settings/log_page.go | 2 +- 18 files changed, 31 insertions(+), 50 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index 52abe731c..a697f2141 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -29,9 +29,9 @@ type SegmentedControl struct { changed bool mu sync.Mutex - isEnableSwipe bool - sliceAction SliceAction - segmentType SegmentType + isSwipeActionEnabled bool + sliceAction SliceAction + segmentType SegmentType } func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType) *SegmentedControl { @@ -39,13 +39,13 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType list.IsHoverable = false sc := &SegmentedControl{ - list: list, - theme: t, - segmentTitles: segmentTitles, - leftNavBtn: t.NewClickable(false), - rightNavBtn: t.NewClickable(false), - isEnableSwipe: true, - segmentType: segmentType, + list: list, + theme: t, + segmentTitles: segmentTitles, + leftNavBtn: t.NewClickable(false), + rightNavBtn: t.NewClickable(false), + isSwipeActionEnabled: true, + segmentType: segmentType, } sc.sliceAction.Draged(func(dragDirection SwipeDirection) { @@ -57,7 +57,7 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType } func (sc *SegmentedControl) SetEnableSwipe(enable bool) { - sc.isEnableSwipe = enable + sc.isSwipeActionEnabled = enable } func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { @@ -74,7 +74,7 @@ func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { }), layout.Rigid(func(gtx C) D { return layout.Inset{Top: values.MarginPadding16}.Layout(gtx, func(gtx C) D { - if sc.isEnableSwipe { + if sc.isSwipeActionEnabled { return sc.sliceAction.DragLayout(gtx, func(gtx C) D { return sc.sliceAction.TransformLayout(gtx, body) }, true) diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 219862734..3e7851f0b 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -196,6 +196,7 @@ func (s *Slider) handleClickEvent() { } else { s.sliceAction.PushRight() } + break } } } diff --git a/ui/page/components/components.go b/ui/page/components/components.go index 1af76cfed..de8ed2bbd 100644 --- a/ui/page/components/components.go +++ b/ui/page/components/components.go @@ -78,24 +78,6 @@ func (c Container) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions return c.Padding.Layout(gtx, w) } -func UniformPadding(gtx layout.Context, body layout.Widget) layout.Dimensions { - width := gtx.Constraints.Max.X - - padding := values.MarginPadding24 - - if (width - 2*gtx.Dp(padding)) > gtx.Dp(values.AppWidth) { - paddingValue := float32(width-gtx.Dp(values.AppWidth)) / 4 - padding = unit.Dp(paddingValue) - } - - return layout.Inset{ - Top: values.MarginPadding24, - Right: padding, - Bottom: values.MarginPadding24, - Left: padding, - }.Layout(gtx, body) -} - func UniformHorizontalPadding(gtx layout.Context, body layout.Widget) layout.Dimensions { width := gtx.Constraints.Max.X diff --git a/ui/page/components/restore_page.go b/ui/page/components/restore_page.go index bfa92ac9b..51d748e01 100644 --- a/ui/page/components/restore_page.go +++ b/ui/page/components/restore_page.go @@ -108,7 +108,7 @@ func (pg *Restore) layoutDesktop(gtx C) D { } return sp.Layout(pg.ParentWindow(), gtx) } - return UniformPadding(gtx, body) + return cryptomaterial.UniformPadding(gtx, body) } func (pg *Restore) layoutMobile(gtx C) D { @@ -130,7 +130,7 @@ func (pg *Restore) layoutMobile(gtx C) D { } func (pg *Restore) restoreLayout(gtx layout.Context) layout.Dimensions { - return UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(pg.tabLayout), layout.Rigid(pg.Theme.Separator().Layout), diff --git a/ui/page/exchange/order_details_page.go b/ui/page/exchange/order_details_page.go index 3d4252071..5e8b8349f 100644 --- a/ui/page/exchange/order_details_page.go +++ b/ui/page/exchange/order_details_page.go @@ -146,7 +146,7 @@ func (pg *OrderDetailsPage) Layout(gtx C) D { return sp.Layout(pg.ParentWindow(), gtx) } - return components.UniformPadding(gtx, container) + return cryptomaterial.UniformPadding(gtx, container) } func (pg *OrderDetailsPage) layout(gtx C) D { diff --git a/ui/page/exchange/order_history_page.go b/ui/page/exchange/order_history_page.go index 985c0abf2..5004664f2 100644 --- a/ui/page/exchange/order_history_page.go +++ b/ui/page/exchange/order_history_page.go @@ -126,7 +126,7 @@ func (pg *OrderHistoryPage) Layout(gtx C) D { }) } - return components.UniformPadding(gtx, container) + return cryptomaterial.UniformPadding(gtx, container) } func (pg *OrderHistoryPage) layout(gtx C) D { diff --git a/ui/page/governance/governance_page.go b/ui/page/governance/governance_page.go index 6fc5a7fbc..631519998 100644 --- a/ui/page/governance/governance_page.go +++ b/ui/page/governance/governance_page.go @@ -120,10 +120,10 @@ func (pg *Page) Layout(gtx C) D { func (pg *Page) layoutDesktop(gtx layout.Context) layout.Dimensions { if !pg.isGovernanceAPIAllowed() { - return components.UniformPadding(gtx, pg.splashScreen) + return cryptomaterial.UniformPadding(gtx, pg.splashScreen) } - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(pg.layoutPageTopNav), layout.Rigid(pg.layoutTabs), diff --git a/ui/page/governance/proposal_details_page.go b/ui/page/governance/proposal_details_page.go index e01d40e2d..76d0ff9f2 100644 --- a/ui/page/governance/proposal_details_page.go +++ b/ui/page/governance/proposal_details_page.go @@ -564,7 +564,7 @@ func (pg *ProposalDetails) layoutDesktop(gtx layout.Context) layout.Dimensions { } return page.Layout(pg.ParentWindow(), gtx) } - return components.UniformPadding(gtx, body) + return cryptomaterial.UniformPadding(gtx, body) } func (pg *ProposalDetails) layoutMobile(gtx layout.Context) layout.Dimensions { diff --git a/ui/page/privacy/account_mixer_page.go b/ui/page/privacy/account_mixer_page.go index 2f3aee519..c254f1349 100644 --- a/ui/page/privacy/account_mixer_page.go +++ b/ui/page/privacy/account_mixer_page.go @@ -332,7 +332,7 @@ func (pg *AccountMixerPage) Layout(gtx layout.Context) layout.Dimensions { } func (pg *AccountMixerPage) layoutDesktop(gtx layout.Context) layout.Dimensions { - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { in := values.MarginPadding50 return layout.Inset{ Top: values.MarginPadding25, diff --git a/ui/page/privacy/manual_mixer_setup_page.go b/ui/page/privacy/manual_mixer_setup_page.go index 3207c5b25..856c510d4 100644 --- a/ui/page/privacy/manual_mixer_setup_page.go +++ b/ui/page/privacy/manual_mixer_setup_page.go @@ -180,7 +180,7 @@ func (pg *ManualMixerSetupPage) Layout(gtx layout.Context) layout.Dimensions { return page.Layout(pg.ParentWindow(), gtx) } - return components.UniformPadding(gtx, body) + return cryptomaterial.UniformPadding(gtx, body) } func (pg *ManualMixerSetupPage) mixerAccountSections(gtx layout.Context, title string, body layout.Widget) layout.Dimensions { diff --git a/ui/page/privacy/setup_mixer_accounts_page.go b/ui/page/privacy/setup_mixer_accounts_page.go index 6fe4135d7..ce4b60ee1 100644 --- a/ui/page/privacy/setup_mixer_accounts_page.go +++ b/ui/page/privacy/setup_mixer_accounts_page.go @@ -148,7 +148,7 @@ func (pg *SetupMixerAccountsPage) Layout(gtx layout.Context) layout.Dimensions { return page.Layout(pg.ParentWindow(), gtx) } - return components.UniformPadding(gtx, body) + return cryptomaterial.UniformPadding(gtx, body) } func (pg *SetupMixerAccountsPage) autoSetupLayout(gtx C) D { diff --git a/ui/page/privacy/setup_privacy_page.go b/ui/page/privacy/setup_privacy_page.go index c3e2a2f3f..730ff069f 100644 --- a/ui/page/privacy/setup_privacy_page.go +++ b/ui/page/privacy/setup_privacy_page.go @@ -65,7 +65,7 @@ func (pg *SetupPrivacyPage) OnNavigatedTo() { // to be eventually drawn on screen. // Part of the load.Page interface. func (pg *SetupPrivacyPage) Layout(gtx layout.Context) layout.Dimensions { - return components.UniformPadding(gtx, pg.privacyIntroLayout) + return cryptomaterial.UniformPadding(gtx, pg.privacyIntroLayout) } func (pg *SetupPrivacyPage) privacyIntroLayout(gtx layout.Context) layout.Dimensions { diff --git a/ui/page/root/overview_page.go b/ui/page/root/overview_page.go index cdf9071d5..012fccdaa 100644 --- a/ui/page/root/overview_page.go +++ b/ui/page/root/overview_page.go @@ -284,7 +284,7 @@ func (pg *OverviewPage) layoutDesktop(gtx layout.Context) layout.Dimensions { pg.recentProposal, } - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return pg.Theme.List(pg.scrollContainer).Layout(gtx, 1, func(gtx C, i int) D { return layout.Center.Layout(gtx, func(gtx C) D { return layout.Inset{Right: values.MarginPadding2}.Layout(gtx, func(gtx C) D { @@ -306,7 +306,7 @@ func (pg *OverviewPage) layoutMobile(gtx C) D { pg.recentProposal, } - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return layout.Center.Layout(gtx, func(gtx C) D { return pg.pageContainer.Layout(gtx, len(pageContent), func(gtx C, i int) D { return pageContent[i](gtx) diff --git a/ui/page/seedbackup/backup_instructions.go b/ui/page/seedbackup/backup_instructions.go index 698438aaa..092b47fbc 100644 --- a/ui/page/seedbackup/backup_instructions.go +++ b/ui/page/seedbackup/backup_instructions.go @@ -194,5 +194,5 @@ func container(gtx C, isMobile bool, theme cryptomaterial.Theme, body layout.Wid if isMobile { return components.UniformMobile(gtx, false, false, bodyLayout) } - return components.UniformPadding(gtx, bodyLayout) + return cryptomaterial.UniformPadding(gtx, bodyLayout) } diff --git a/ui/page/seedbackup/backup_success.go b/ui/page/seedbackup/backup_success.go index d9578bc06..70f1763f6 100644 --- a/ui/page/seedbackup/backup_success.go +++ b/ui/page/seedbackup/backup_success.go @@ -6,7 +6,6 @@ import ( "github.com/crypto-power/cryptopower/app" "github.com/crypto-power/cryptopower/ui/cryptomaterial" "github.com/crypto-power/cryptopower/ui/load" - "github.com/crypto-power/cryptopower/ui/page/components" "github.com/crypto-power/cryptopower/ui/values" ) @@ -67,7 +66,7 @@ func (pg *BackupSuccessPage) OnNavigatedFrom() {} // to be eventually drawn on screen. // Part of the load.Page interface. func (pg *BackupSuccessPage) Layout(gtx C) D { - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return cryptomaterial.LinearLayout{ Width: cryptomaterial.MatchParent, Height: cryptomaterial.MatchParent, diff --git a/ui/page/send/manual_coin_selection.go b/ui/page/send/manual_coin_selection.go index e88331bc6..b873765a9 100644 --- a/ui/page/send/manual_coin_selection.go +++ b/ui/page/send/manual_coin_selection.go @@ -14,7 +14,6 @@ import ( libutils "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/crypto-power/cryptopower/ui/cryptomaterial" "github.com/crypto-power/cryptopower/ui/load" - "github.com/crypto-power/cryptopower/ui/page/components" "github.com/crypto-power/cryptopower/ui/values" ) @@ -364,7 +363,7 @@ func (pg *ManualCoinSelectionPage) Layout(gtx C) D { layout.Expanded(func(gtx C) D { return layout.Stack{Alignment: layout.NE}.Layout(gtx, layout.Expanded(func(gtx C) D { - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return cryptomaterial.LinearLayout{ Width: cryptomaterial.WrapContent, Height: cryptomaterial.WrapContent, diff --git a/ui/page/settings/btc_account_details_page.go b/ui/page/settings/btc_account_details_page.go index fb1b73c22..e5a6e0367 100644 --- a/ui/page/settings/btc_account_details_page.go +++ b/ui/page/settings/btc_account_details_page.go @@ -205,7 +205,7 @@ func (pg *BTCAcctDetailsPage) layoutDesktop(gtx layout.Context, widgets []func(g } return sp.Layout(pg.ParentWindow(), gtx) } - return components.UniformPadding(gtx, body) + return cryptomaterial.UniformPadding(gtx, body) } func (pg *BTCAcctDetailsPage) layoutMobile(gtx layout.Context, widgets []func(gtx C) D) layout.Dimensions { diff --git a/ui/page/settings/log_page.go b/ui/page/settings/log_page.go index 1916306c5..a30e3c1f4 100644 --- a/ui/page/settings/log_page.go +++ b/ui/page/settings/log_page.go @@ -162,7 +162,7 @@ func (pg *LogPage) layoutDesktop(gtx layout.Context) layout.Dimensions { if pg.title == values.String(values.StrWalletLog) { return container(gtx) } - return components.UniformPadding(gtx, container) + return cryptomaterial.UniformPadding(gtx, container) } func (pg *LogPage) layoutMobile(gtx layout.Context) layout.Dimensions { From 69afe4faca275e8775ea282f8a75693935a05f75 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Thu, 23 Nov 2023 18:23:00 +0700 Subject: [PATCH 06/13] add comment for function --- ui/cryptomaterial/slide_action.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 655d96a2e..91b7dc9ee 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -179,6 +179,8 @@ func smooth(t float32) float32 { } // easeInOutCubic maps a linear value to a ease-in-out-cubic easing function. +// It is a mathematical function that describes how a value changes over time. +// It can be applied to adjusting the speed of animation func easeInOutCubic(t float32) float32 { if t < 0.5 { return 4 * t * t * t From 97ef9e88c22aaa759e93d58ea6a5e054483b4879 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Thu, 30 Nov 2023 23:41:33 +0700 Subject: [PATCH 07/13] update segmented control and slide action --- ui/cryptomaterial/segmented_control.go | 56 ++++++++++++++++---------- ui/cryptomaterial/slide_action.go | 45 +++++++++++++++------ ui/cryptomaterial/slider.go | 3 +- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index a697f2141..f1dd18e12 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -30,7 +30,8 @@ type SegmentedControl struct { mu sync.Mutex isSwipeActionEnabled bool - sliceAction SliceAction + sliceAction *SliceAction + sliceActionTitle *SliceAction segmentType SegmentType } @@ -46,6 +47,8 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType rightNavBtn: t.NewClickable(false), isSwipeActionEnabled: true, segmentType: segmentType, + sliceAction: NewSliceAction(), + sliceActionTitle: NewSliceAction(), } sc.sliceAction.Draged(func(dragDirection SwipeDirection) { @@ -53,6 +56,13 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType sc.handleActionEvent(isNext) }) + sc.sliceActionTitle.SetDragEffect(50) + + sc.sliceActionTitle.Draged(func(dragDirection SwipeDirection) { + isNext := dragDirection == SwipeLeft + sc.handleActionEvent(isNext) + }) + return sc } @@ -96,27 +106,29 @@ func (sc *SegmentedControl) GroupTileLayout(gtx C) D { Border: Border{Radius: Radius(8)}, }.Layout(gtx, layout.Rigid(func(gtx C) D { - return sc.list.Layout(gtx, len(sc.segmentTitles), func(gtx C, i int) D { - isSelectedSegment := sc.SelectedIndex() == i - return layout.Center.Layout(gtx, func(gtx C) D { - bg := sc.theme.Color.SurfaceHighlight - txt := sc.theme.DecoratedText(values.TextSize16, sc.segmentTitles[i], sc.theme.Color.GrayText1, font.SemiBold) - border := Border{Radius: Radius(0)} - if isSelectedSegment { - bg = sc.theme.Color.Surface - txt.Color = sc.theme.Color.Text - border = Border{Radius: Radius(8)} - } - return LinearLayout{ - Width: WrapContent, - Height: WrapContent, - Padding: layout.UniformInset(values.MarginPadding8), - Background: bg, - Margin: layout.UniformInset(values.MarginPadding5), - Border: border, - }.Layout2(gtx, txt.Layout) + return sc.sliceActionTitle.DragLayout(gtx, func(gtx C) D { + return sc.list.Layout(gtx, len(sc.segmentTitles), func(gtx C, i int) D { + isSelectedSegment := sc.SelectedIndex() == i + return layout.Center.Layout(gtx, func(gtx C) D { + bg := sc.theme.Color.SurfaceHighlight + txt := sc.theme.DecoratedText(values.TextSize16, sc.segmentTitles[i], sc.theme.Color.GrayText1, font.SemiBold) + border := Border{Radius: Radius(0)} + if isSelectedSegment { + bg = sc.theme.Color.Surface + txt.Color = sc.theme.Color.Text + border = Border{Radius: Radius(8)} + } + return LinearLayout{ + Width: WrapContent, + Height: WrapContent, + Padding: layout.UniformInset(values.MarginPadding8), + Background: bg, + Margin: layout.UniformInset(values.MarginPadding5), + Border: border, + }.Layout2(gtx, txt.Layout) + }) }) - }) + }, true) }), ) } @@ -236,6 +248,7 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { sc.selectedIndex++ } sc.sliceAction.PushLeft() + sc.sliceActionTitle.PushLeft() } else { if sc.selectedIndex == 0 { sc.selectedIndex = l @@ -243,6 +256,7 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { sc.selectedIndex-- } sc.sliceAction.PushRight() + sc.sliceActionTitle.PushRight() } sc.changed = true } diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 91b7dc9ee..194650617 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -12,7 +12,10 @@ import ( "gioui.org/op/clip" ) -const defaultDuration = 300 * time.Millisecond +const ( + defaultDuration = 500 * time.Millisecond + defaultdragEffect = 100 +) type SwipeDirection int @@ -24,16 +27,18 @@ const ( type Dragged func(dragDirection SwipeDirection) type SliceAction struct { - duration time.Duration - push int - next *op.Ops - nextCall op.CallOp - lastCall op.CallOp + Duration time.Duration + IsReverse bool + push int + next *op.Ops + nextCall op.CallOp + lastCall op.CallOp t0 time.Time offset float32 // animation state + dragEffect int dragStarted f32.Point dragOffset int drag gesture.Drag @@ -41,12 +46,21 @@ type SliceAction struct { isPushing bool } +func NewSliceAction() *SliceAction { + return &SliceAction{ + Duration: defaultDuration, + dragEffect: defaultdragEffect, + } +} + // PushLeft pushes the existing widget to the left. func (s *SliceAction) PushLeft() { s.push = 1 } // PushRight pushes the existing widget to the right. func (s *SliceAction) PushRight() { s.push = -1 } +func (s *SliceAction) SetDragEffect(offset int) { s.dragEffect = offset } + func (s *SliceAction) Draged(drag Dragged) { s.draged = drag } @@ -59,12 +73,12 @@ func (s *SliceAction) DragLayout(gtx C, w layout.Widget, isWrapContent bool) D { s.dragOffset = 0 case pointer.Drag: newOffset := int(s.dragStarted.X - event.Position.X) - if newOffset > 100 { + if newOffset > s.dragEffect { if !s.isPushing && s.draged != nil { s.isPushing = true s.draged(SwipeLeft) } - } else if newOffset < -100 { + } else if newOffset < -s.dragEffect { if !s.isPushing && s.draged != nil { s.isPushing = true s.draged(SwipeRight) @@ -106,7 +120,7 @@ func (s *SliceAction) TransformLayout(gtx C, w layout.Widget) D { } if s.offset != 0 { - duration := s.duration + duration := s.Duration if duration == 0 { duration = defaultDuration } @@ -146,24 +160,29 @@ func (s *SliceAction) TransformLayout(gtx C, w layout.Widget) D { offset := smooth(s.offset) + reverse := 1 + if s.IsReverse { + reverse = -1 + } + if s.offset > 0 { defer op.Offset(image.Point{ - X: int(float32(dims.Size.X) * (offset - 1)), + X: int(float32(dims.Size.X)*(offset-1)) * reverse, }).Push(gtx.Ops).Pop() s.lastCall.Add(gtx.Ops) defer op.Offset(image.Point{ - X: dims.Size.X, + X: dims.Size.X * reverse, }).Push(gtx.Ops).Pop() s.nextCall.Add(gtx.Ops) } else { defer op.Offset(image.Point{ - X: int(float32(dims.Size.X) * (offset + 1)), + X: int(float32(dims.Size.X)*(offset+1)) * reverse, }).Push(gtx.Ops).Pop() s.lastCall.Add(gtx.Ops) defer op.Offset(image.Point{ - X: -dims.Size.X, + X: -dims.Size.X * reverse, }).Push(gtx.Ops).Pop() s.nextCall.Add(gtx.Ops) } diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 3e7851f0b..9254736aa 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -29,7 +29,7 @@ type Slider struct { ButtonBackgroundColor color.NRGBA IndicatorBackgroundColor color.NRGBA SelectedIndicatorColor color.NRGBA // this is a full color no opacity - sliceAction SliceAction + sliceAction *SliceAction } var m4 = values.MarginPadding4 @@ -44,6 +44,7 @@ func (t *Theme) Slider() *Slider { ButtonBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), IndicatorBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), SelectedIndicatorColor: t.Color.White, + sliceAction: NewSliceAction(), } sl.card = sl.t.Card() From d463db983e68ef6c39a3bd3875e85f3498680c13 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Fri, 1 Dec 2023 23:48:34 +0700 Subject: [PATCH 08/13] update segmented control, fix drag action on slide action, fix drag on slider, update segmented for transaction tab --- ui/cryptomaterial/segmented_control.go | 10 +- ui/cryptomaterial/slide_action.go | 66 ++++++----- ui/cryptomaterial/slider.go | 19 ++- ui/page/root/main_page.go | 2 +- ui/page/root/overview_page.go | 143 ++++++++++++++--------- ui/page/root/trade_page.go | 2 +- ui/page/transaction/transactions_page.go | 21 ++-- ui/utils/utils.go | 13 +++ 8 files changed, 164 insertions(+), 112 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index 05f990b40..2a55fdac8 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -12,8 +12,8 @@ import ( type SegmentType int const ( - Group SegmentType = iota - Split + SegmentTypeGroup SegmentType = iota + SegmentTypeSplit ) type SegmentedControl struct { @@ -77,7 +77,7 @@ func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { Alignment: layout.Middle, }.Layout(gtx, layout.Rigid(func(gtx C) D { - if sc.segmentType == Group { + if sc.segmentType == SegmentTypeGroup { return sc.GroupTileLayout(gtx) } return sc.splitTileLayout(gtx) @@ -87,7 +87,7 @@ func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { if sc.isSwipeActionEnabled { return sc.sliceAction.DragLayout(gtx, func(gtx C) D { return sc.sliceAction.TransformLayout(gtx, body) - }, true) + }) } return body(gtx) }) @@ -128,7 +128,7 @@ func (sc *SegmentedControl) GroupTileLayout(gtx C) D { }.Layout2(gtx, txt.Layout) }) }) - }, true) + }) }), ) } diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 194650617..1ca1ed639 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -65,42 +65,48 @@ func (s *SliceAction) Draged(drag Dragged) { s.draged = drag } -func (s *SliceAction) DragLayout(gtx C, w layout.Widget, isWrapContent bool) D { - for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { - switch event.Type { - case pointer.Press: - s.dragStarted = event.Position - s.dragOffset = 0 - case pointer.Drag: - newOffset := int(s.dragStarted.X - event.Position.X) - if newOffset > s.dragEffect { - if !s.isPushing && s.draged != nil { - s.isPushing = true - s.draged(SwipeLeft) - } - } else if newOffset < -s.dragEffect { - if !s.isPushing && s.draged != nil { - s.isPushing = true - s.draged(SwipeRight) +func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { + if gtx.Queue != nil { + for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { + switch event.Type { + case pointer.Press: + s.dragStarted = event.Position + s.dragOffset = 0 + case pointer.Drag: + newOffset := int(s.dragStarted.X - event.Position.X) + if newOffset > s.dragEffect { + if !s.isPushing && s.draged != nil { + s.isPushing = true + s.draged(SwipeLeft) + } + } else if newOffset < -s.dragEffect { + if !s.isPushing && s.draged != nil { + s.isPushing = true + s.draged(SwipeRight) + } } + s.dragOffset = newOffset + case pointer.Release: + fallthrough + case pointer.Cancel: + s.isPushing = false } - s.dragOffset = newOffset - case pointer.Release: - fallthrough - case pointer.Cancel: - s.isPushing = false } } - - if isWrapContent { - area := clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops) - s.drag.Add(gtx.Ops) - defer area.Pop() - } else { - s.drag.Add(gtx.Ops) + var dims layout.Dimensions + var call op.CallOp + { + m := op.Record(gtx.Ops) + dims = w(gtx) + call = m.Stop() } - return w(gtx) + area := clip.Rect(image.Rect(0, 0, dims.Size.X, dims.Size.Y)).Push(gtx.Ops) + s.drag.Add(gtx.Ops) + defer area.Pop() + + call.Add(gtx.Ops) + return dims } func (s *SliceAction) TransformLayout(gtx C, w layout.Widget) D { diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 9254736aa..3cb0f7b26 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -87,13 +87,12 @@ func (s *Slider) Layout(gtx C, items []layout.Widget) D { } s.handleClickEvent() - gtx.Constraints.Max = s.slideItems[s.selected].widgetItem(gtx).Size - return layout.Stack{Alignment: layout.S}.Layout(gtx, - layout.Expanded(func(gtx C) D { - return s.sliceAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) - }), - layout.Stacked(func(gtx C) D { - return s.sliceAction.DragLayout(gtx, func(gtx C) D { + return s.sliceAction.DragLayout(gtx, func(gtx C) D { + return layout.Stack{Alignment: layout.S}.Layout(gtx, + layout.Expanded(func(gtx C) D { + return s.sliceAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) + }), + layout.Stacked(func(gtx C) D { return layout.Inset{ Right: values.MarginPadding15, Left: values.MarginPadding15, @@ -108,9 +107,9 @@ func (s *Slider) Layout(gtx C, items []layout.Widget) D { }), ) }) - }, false) - }), - ) + }), + ) + }) } func (s *Slider) buttonLayout(gtx C) D { diff --git a/ui/page/root/main_page.go b/ui/page/root/main_page.go index b12da89e2..d8a79804d 100644 --- a/ui/page/root/main_page.go +++ b/ui/page/root/main_page.go @@ -165,7 +165,7 @@ func (mp *MainPage) initTabOptions() { commonTabs = append(commonTabs[:4], append(dcrSpecificTabs, commonTabs[4:]...)...) } - mp.pageNavigationTab = mp.Theme.SegmentedControl(commonTabs, cryptomaterial.Split) + mp.pageNavigationTab = mp.Theme.SegmentedControl(commonTabs, cryptomaterial.SegmentTypeSplit) } func (mp *MainPage) isGovernanceAPIAllowed() bool { diff --git a/ui/page/root/overview_page.go b/ui/page/root/overview_page.go index a797d34b1..b47982472 100644 --- a/ui/page/root/overview_page.go +++ b/ui/page/root/overview_page.go @@ -7,6 +7,7 @@ import ( "strings" "gioui.org/layout" + "gioui.org/op" "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" @@ -336,98 +337,126 @@ func (pg *OverviewPage) sliderLayout(gtx C) D { layout.Rigid(func(gtx C) D { // Only show mixer slider if mixer is running if len(pg.mixerSliderData) == 0 { - return pg.assetBalanceSliderLayout(gtx) + return pg.assetBalanceSliderLayout(gtx, 0) } if pg.Load.IsMobileView() { return layout.Flex{Axis: axis}.Layout(gtx, - layout.Rigid(pg.assetBalanceSliderLayout), + layout.Rigid(func(gtx C) D { + return pg.assetBalanceSliderLayout(gtx, 0) + }), layout.Rigid(func(gtx C) D { return layout.Inset{Top: values.MarginPadding10}.Layout(gtx, pg.mixerSliderLayout) }), ) } + cgtx := gtx + cgtx.Constraints.Max.X = gtx.Constraints.Max.X/2 - cgtx.Dp(10) + macro := op.Record(cgtx.Ops) + mixerSliderDims := pg.mixerSliderLayout(cgtx) + call := macro.Stop() return layout.Flex{}.Layout(gtx, - layout.Flexed(.5, pg.assetBalanceSliderLayout), layout.Flexed(.5, func(gtx C) D { - return layout.Inset{Left: values.MarginPadding10}.Layout(gtx, pg.mixerSliderLayout) + return pg.assetBalanceSliderLayout(gtx, mixerSliderDims.Size.Y) + }), + layout.Flexed(.5, func(gtx C) D { + return layout.Inset{Left: values.MarginPadding10}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + call.Add(gtx.Ops) + return mixerSliderDims + }) }), ) }), ) } -func (pg *OverviewPage) assetBalanceSliderLayout(gtx C) D { +func (pg *OverviewPage) assetBalanceSliderLayout(gtx C, rowHeigh int) D { var sliderWidget []layout.Widget if pg.dcr != nil { - sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.dcr)) + sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.dcr, rowHeigh)) } if pg.btc != nil { - sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.btc)) + sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.btc, rowHeigh)) } if pg.ltc != nil { - sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.ltc)) + sliderWidget = append(sliderWidget, pg.assetBalanceItemLayout(pg.ltc, rowHeigh)) } return pg.assetBalanceSlider.Layout(gtx, sliderWidget) } -func (pg *OverviewPage) assetBalanceItemLayout(item *assetBalanceSliderItem) layout.Widget { +func (pg *OverviewPage) assetBalanceItemLayout(item *assetBalanceSliderItem, rowHeigh int) layout.Widget { return func(gtx C) D { - return pg.sliderRedirectBtn.Layout(gtx, func(gtx C) D { - return layout.Stack{}.Layout(gtx, - layout.Stacked(func(gtx C) D { - width := gtx.Constraints.Max.X - height := width / item.backgroundImage.AspectRatio() // maintain aspect ratio - return item.backgroundImage.LayoutSizeWithRadius(gtx, gtx.Metric.PxToDp(width), gtx.Metric.PxToDp(height), 8) - }), - layout.Expanded(func(gtx C) D { - col := pg.Theme.Color.InvText - return layout.Flex{ - Axis: layout.Vertical, - Alignment: layout.Middle, - }.Layout(gtx, - layout.Rigid(func(gtx C) D { - lbl := pg.Theme.Body1(item.assetType) - lbl.Color = col - return pg.centerLayout(gtx, values.MarginPadding15, values.MarginPadding10, lbl.Layout) - }), - layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding10, func(gtx C) D { - return item.image.LayoutSize(gtx, values.MarginPadding65) - }) - }), - layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding10, func(gtx C) D { - return components.LayoutBalanceColorWithState(gtx, pg.Load, item.totalBalance.String(), col) - }) - }), - layout.Rigid(func(gtx C) D { - card := pg.Theme.Card() - card.Radius = cryptomaterial.Radius(12) - card.Color = values.TransparentColor(values.TransparentBlack, 0.2) - return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding0, func(gtx C) D { - return card.Layout(gtx, func(gtx C) D { - return layout.Inset{ - Top: values.MarginPadding4, - Bottom: values.MarginPadding4, - Right: values.MarginPadding8, - Left: values.MarginPadding8, - }.Layout(gtx, func(gtx C) D { - return components.LayoutBalanceColorWithStateUSD(gtx, pg.Load, item.totalBalanceUSD, col) - }) - }) - }) - }), - ) - }), - ) + return utils.RadiusLayout(gtx, 8, func(gtx C) D { + return pg.sliderRedirectBtn.Layout(gtx, func(gtx C) D { + size := pg.contentSliderLayout(item)(gtx).Size + if size.Y < rowHeigh { + size.Y = rowHeigh + } + return layout.Stack{}.Layout(gtx, + layout.Stacked(func(gtx C) D { + width := gtx.Constraints.Max.X + height := width / item.backgroundImage.AspectRatio() // maintain aspect ratio + if height < size.Y { + height = size.Y + width = height * item.backgroundImage.AspectRatio() + } + return item.backgroundImage.LayoutSize2(gtx, gtx.Metric.PxToDp(width), gtx.Metric.PxToDp(height)) + }), + layout.Expanded(func(gtx C) D { + return pg.contentSliderLayout(item)(gtx) + }), + ) + }) }) } } +func (pg *OverviewPage) contentSliderLayout(item *assetBalanceSliderItem) layout.Widget { + col := pg.Theme.Color.InvText + return func(gtx C) D { + return layout.Flex{ + Axis: layout.Vertical, + Alignment: layout.Middle, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + lbl := pg.Theme.Body1(item.assetType) + lbl.Color = col + return pg.centerLayout(gtx, values.MarginPadding15, values.MarginPadding10, lbl.Layout) + }), + layout.Rigid(func(gtx C) D { + return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding10, func(gtx C) D { + return item.image.LayoutSize(gtx, values.MarginPadding65) + }) + }), + layout.Rigid(func(gtx C) D { + return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding10, func(gtx C) D { + return components.LayoutBalanceColorWithState(gtx, pg.Load, item.totalBalance.String(), col) + }) + }), + layout.Rigid(func(gtx C) D { + card := pg.Theme.Card() + card.Radius = cryptomaterial.Radius(12) + card.Color = values.TransparentColor(values.TransparentBlack, 0.2) + return pg.centerLayout(gtx, values.MarginPadding0, values.MarginPadding0, func(gtx C) D { + return card.Layout(gtx, func(gtx C) D { + return layout.Inset{ + Top: values.MarginPadding4, + Bottom: values.MarginPadding4, + Right: values.MarginPadding8, + Left: values.MarginPadding8, + }.Layout(gtx, func(gtx C) D { + return components.LayoutBalanceColorWithStateUSD(gtx, pg.Load, item.totalBalanceUSD, col) + }) + }) + }) + }), + ) + } +} + func (pg *OverviewPage) mixerSliderLayout(gtx C) D { sliderWidget := make([]layout.Widget, 0) for _, key := range pg.sortedMixerSlideKeys { diff --git a/ui/page/root/trade_page.go b/ui/page/root/trade_page.go index 4e0f31fba..eacf8e06a 100644 --- a/ui/page/root/trade_page.go +++ b/ui/page/root/trade_page.go @@ -49,7 +49,7 @@ func NewTradePage(l *load.Load) *TradePage { }, } - pg.tab = l.Theme.SegmentedControl(tabTitles, cryptomaterial.Group) + pg.tab = l.Theme.SegmentedControl(tabTitles, cryptomaterial.SegmentTypeGroup) rad := cryptomaterial.Radius(14) pg.exchangeBtn = l.Theme.NewClickable(false) diff --git a/ui/page/transaction/transactions_page.go b/ui/page/transaction/transactions_page.go index bfb595cae..1afc17884 100644 --- a/ui/page/transaction/transactions_page.go +++ b/ui/page/transaction/transactions_page.go @@ -79,7 +79,7 @@ func NewTransactionsPage(l *load.Load, isHomepageLayout bool) *TransactionsPage separator: l.Theme.Separator(), transactionList: l.Theme.NewClickableList(layout.Vertical), isHomepageLayout: isHomepageLayout, - txCategoryTab: l.Theme.SegmentedControl(txTabs), + txCategoryTab: l.Theme.SegmentedControl(txTabs, cryptomaterial.SegmentTypeGroup), } // init the asset selector @@ -367,26 +367,31 @@ func (pg *TransactionsPage) txListLayout(gtx C) D { } func (pg *TransactionsPage) layoutDesktop(gtx C) D { - items := []layout.FlexChild{} isDCRAssetSelected := pg.selectedWallet != nil && pg.selectedWallet.GetAssetType() == utils.DCRWalletAsset if isDCRAssetSelected || (pg.dcrWalletExists && pg.selectedWallet == nil) { // Only show tx category navigation txCategoryTab for DCR wallets. - items = append(items, layout.Rigid(pg.txCategoriesNav)) + return pg.txCategoryTab.Layout(gtx, pg.layoutBody) } + items := []layout.FlexChild{} items = append(items, layout.Rigid(pg.desktopLayoutContent)) if pg.isHomepageLayout { - return components.UniformPadding(gtx, func(gtx C) D { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, items...) }) } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, items...) } -func (pg *TransactionsPage) txCategoriesNav(gtx C) D { - return cryptomaterial.CentralizeWidget(gtx, func(gtx C) D { - return layout.Inset{Bottom: values.MarginPadding16}.Layout(gtx, pg.txCategoryTab.Layout) - }) +func (pg *TransactionsPage) layoutBody(gtx C) D { + items := []layout.FlexChild{} + items = append(items, layout.Rigid(pg.desktopLayoutContent)) + if pg.isHomepageLayout { + return cryptomaterial.UniformPadding(gtx, func(gtx C) D { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, items...) + }) + } + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, items...) } func (pg *TransactionsPage) desktopLayoutContent(gtx C) D { diff --git a/ui/utils/utils.go b/ui/utils/utils.go index 315d8c312..ebe6a53d9 100644 --- a/ui/utils/utils.go +++ b/ui/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "image" "net" "net/url" "os" @@ -14,6 +15,9 @@ import ( "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/crypto-power/cryptopower/ui/cryptomaterial" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" "gioui.org/widget" "golang.org/x/text/message" ) @@ -160,3 +164,12 @@ func StringNotEmpty(texts ...string) bool { return true } + +func RadiusLayout(gtx layout.Context, radius int, w layout.Widget) layout.Dimensions { + m := op.Record(gtx.Ops) + dims := w(gtx) + call := m.Stop() + defer clip.UniformRRect(image.Rectangle{Max: dims.Size}, radius).Push(gtx.Ops).Pop() + call.Add(gtx.Ops) + return dims +} From 4eb951b3364b671136262478dd0867c460562688 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Sat, 2 Dec 2023 10:17:55 +0700 Subject: [PATCH 09/13] update slider and clean code --- ui/cryptomaterial/segmented_control.go | 34 +++++++++++++------------- ui/cryptomaterial/slide_action.go | 18 +++++++------- ui/cryptomaterial/slider.go | 25 ++++++++++--------- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index 2a55fdac8..db4a4efd0 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -30,8 +30,8 @@ type SegmentedControl struct { mu sync.Mutex isSwipeActionEnabled bool - sliceAction *SliceAction - sliceActionTitle *SliceAction + slideAction *SlideAction + slideActionTitle *SlideAction segmentType SegmentType } @@ -47,18 +47,18 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType rightNavBtn: t.NewClickable(false), isSwipeActionEnabled: true, segmentType: segmentType, - sliceAction: NewSliceAction(), - sliceActionTitle: NewSliceAction(), + slideAction: NewSliceAction(), + slideActionTitle: NewSliceAction(), } - sc.sliceAction.Draged(func(dragDirection SwipeDirection) { + sc.slideAction.Draged(func(dragDirection SwipeDirection) { isNext := dragDirection == SwipeLeft sc.handleActionEvent(isNext) }) - sc.sliceActionTitle.SetDragEffect(50) + sc.slideActionTitle.SetDragEffect(50) - sc.sliceActionTitle.Draged(func(dragDirection SwipeDirection) { + sc.slideActionTitle.Draged(func(dragDirection SwipeDirection) { isNext := dragDirection == SwipeLeft sc.handleActionEvent(isNext) }) @@ -84,12 +84,12 @@ func (sc *SegmentedControl) Layout(gtx C, body func(gtx C) D) D { }), layout.Rigid(func(gtx C) D { return layout.Inset{Top: values.MarginPadding16}.Layout(gtx, func(gtx C) D { - if sc.isSwipeActionEnabled { - return sc.sliceAction.DragLayout(gtx, func(gtx C) D { - return sc.sliceAction.TransformLayout(gtx, body) - }) + if !sc.isSwipeActionEnabled { + return body(gtx) } - return body(gtx) + return sc.slideAction.DragLayout(gtx, func(gtx C) D { + return sc.slideAction.TransformLayout(gtx, body) + }) }) }), ) @@ -106,7 +106,7 @@ func (sc *SegmentedControl) GroupTileLayout(gtx C) D { Border: Border{Radius: Radius(8)}, }.Layout(gtx, layout.Rigid(func(gtx C) D { - return sc.sliceActionTitle.DragLayout(gtx, func(gtx C) D { + return sc.slideActionTitle.DragLayout(gtx, func(gtx C) D { return sc.list.Layout(gtx, len(sc.segmentTitles), func(gtx C, i int) D { isSelectedSegment := sc.SelectedIndex() == i return layout.Center.Layout(gtx, func(gtx C) D { @@ -247,16 +247,16 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { } else { sc.selectedIndex++ } - sc.sliceAction.PushLeft() - sc.sliceActionTitle.PushLeft() + sc.slideAction.PushLeft() + sc.slideActionTitle.PushLeft() } else { if sc.selectedIndex == 0 { sc.selectedIndex = l } else { sc.selectedIndex-- } - sc.sliceAction.PushRight() - sc.sliceActionTitle.PushRight() + sc.slideAction.PushRight() + sc.slideActionTitle.PushRight() } sc.changed = true } diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 1ca1ed639..e92293f0b 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -26,7 +26,7 @@ const ( type Dragged func(dragDirection SwipeDirection) -type SliceAction struct { +type SlideAction struct { Duration time.Duration IsReverse bool push int @@ -46,26 +46,26 @@ type SliceAction struct { isPushing bool } -func NewSliceAction() *SliceAction { - return &SliceAction{ +func NewSliceAction() *SlideAction { + return &SlideAction{ Duration: defaultDuration, dragEffect: defaultdragEffect, } } // PushLeft pushes the existing widget to the left. -func (s *SliceAction) PushLeft() { s.push = 1 } +func (s *SlideAction) PushLeft() { s.push = 1 } // PushRight pushes the existing widget to the right. -func (s *SliceAction) PushRight() { s.push = -1 } +func (s *SlideAction) PushRight() { s.push = -1 } -func (s *SliceAction) SetDragEffect(offset int) { s.dragEffect = offset } +func (s *SlideAction) SetDragEffect(offset int) { s.dragEffect = offset } -func (s *SliceAction) Draged(drag Dragged) { +func (s *SlideAction) Draged(drag Dragged) { s.draged = drag } -func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { +func (s *SlideAction) DragLayout(gtx C, w layout.Widget) D { if gtx.Queue != nil { for _, event := range s.drag.Events(gtx.Metric, gtx.Queue, gesture.Horizontal) { switch event.Type { @@ -109,7 +109,7 @@ func (s *SliceAction) DragLayout(gtx C, w layout.Widget) D { return dims } -func (s *SliceAction) TransformLayout(gtx C, w layout.Widget) D { +func (s *SlideAction) TransformLayout(gtx C, w layout.Widget) D { if s.push != 0 { s.next = nil s.lastCall = s.nextCall diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 3cb0f7b26..45bbf9c83 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -29,7 +29,7 @@ type Slider struct { ButtonBackgroundColor color.NRGBA IndicatorBackgroundColor color.NRGBA SelectedIndicatorColor color.NRGBA // this is a full color no opacity - sliceAction *SliceAction + slideAction *SlideAction } var m4 = values.MarginPadding4 @@ -44,13 +44,13 @@ func (t *Theme) Slider() *Slider { ButtonBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), IndicatorBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), SelectedIndicatorColor: t.Color.White, - sliceAction: NewSliceAction(), + slideAction: NewSliceAction(), } sl.card = sl.t.Card() sl.card.Radius = Radius(8) - sl.sliceAction.Draged(func(dragDirection SwipeDirection) { + sl.slideAction.Draged(func(dragDirection SwipeDirection) { isNext := dragDirection == SwipeLeft sl.handleActionEvent(isNext) }) @@ -63,7 +63,7 @@ func (s *Slider) GetSelectedIndex() int { return s.selected } -func (s *Slider) getSliceItems(items []layout.Widget) []*sliderItem { +func (s *Slider) sliderItems(items []layout.Widget) []*sliderItem { slideItems := make([]*sliderItem, 0) for _, item := range items { slideItems = append(slideItems, &sliderItem{ @@ -78,7 +78,7 @@ func (s *Slider) getSliceItems(items []layout.Widget) []*sliderItem { func (s *Slider) Layout(gtx C, items []layout.Widget) D { // set slider items once since layout is drawn multiple times per sec. if !s.isSliderItemsSet { - s.slideItems = s.getSliceItems(items) + s.slideItems = s.sliderItems(items) s.isSliderItemsSet = true } @@ -87,10 +87,10 @@ func (s *Slider) Layout(gtx C, items []layout.Widget) D { } s.handleClickEvent() - return s.sliceAction.DragLayout(gtx, func(gtx C) D { + return s.slideAction.DragLayout(gtx, func(gtx C) D { return layout.Stack{Alignment: layout.S}.Layout(gtx, layout.Expanded(func(gtx C) D { - return s.sliceAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) + return s.slideAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) }), layout.Stacked(func(gtx C) D { return layout.Inset{ @@ -192,9 +192,9 @@ func (s *Slider) handleClickEvent() { lastSelected := s.selected s.selected = i if lastSelected < i { - s.sliceAction.PushLeft() + s.slideAction.PushLeft() } else { - s.sliceAction.PushRight() + s.slideAction.PushRight() } break } @@ -202,6 +202,9 @@ func (s *Slider) handleClickEvent() { } func (s *Slider) handleActionEvent(isNext bool) { + if len(s.slideItems) == 1 { + return + } l := len(s.slideItems) - 1 // index starts at 0 if isNext { if s.selected == l { @@ -209,13 +212,13 @@ func (s *Slider) handleActionEvent(isNext bool) { } else { s.selected++ } - s.sliceAction.PushLeft() + s.slideAction.PushLeft() } else { if s.selected == 0 { s.selected = l } else { s.selected-- } - s.sliceAction.PushRight() + s.slideAction.PushRight() } } From aaca0328ad1a2bd68f171ae0317aa69d530d7fd4 Mon Sep 17 00:00:00 2001 From: Justin Do Date: Sun, 3 Dec 2023 00:05:25 +0700 Subject: [PATCH 10/13] update function name of slide action --- ui/cryptomaterial/segmented_control.go | 4 ++-- ui/cryptomaterial/slide_action.go | 2 +- ui/cryptomaterial/slider.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index db4a4efd0..a64277693 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -47,8 +47,8 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType rightNavBtn: t.NewClickable(false), isSwipeActionEnabled: true, segmentType: segmentType, - slideAction: NewSliceAction(), - slideActionTitle: NewSliceAction(), + slideAction: NewSlideAction(), + slideActionTitle: NewSlideAction(), } sc.slideAction.Draged(func(dragDirection SwipeDirection) { diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index e92293f0b..16724aa69 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -46,7 +46,7 @@ type SlideAction struct { isPushing bool } -func NewSliceAction() *SlideAction { +func NewSlideAction() *SlideAction { return &SlideAction{ Duration: defaultDuration, dragEffect: defaultdragEffect, diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 45bbf9c83..3fe556864 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -44,7 +44,7 @@ func (t *Theme) Slider() *Slider { ButtonBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), IndicatorBackgroundColor: values.TransparentColor(values.TransparentWhite, 0.2), SelectedIndicatorColor: t.Color.White, - slideAction: NewSliceAction(), + slideAction: NewSlideAction(), } sl.card = sl.t.Card() From 04b853bee0a39a2f5d77437f2f5f73bb28f1a1ac Mon Sep 17 00:00:00 2001 From: Justin Do Date: Tue, 5 Dec 2023 15:17:06 +0700 Subject: [PATCH 11/13] update logic on slider and segmented control --- ui/cryptomaterial/segmented_control.go | 8 ++++++++ ui/cryptomaterial/slide_action.go | 4 ++++ ui/cryptomaterial/slider.go | 3 +++ 3 files changed, 15 insertions(+) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index a64277693..769aad40a 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -33,6 +33,8 @@ type SegmentedControl struct { slideAction *SlideAction slideActionTitle *SlideAction segmentType SegmentType + + IsAllowsCycle bool } func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType) *SegmentedControl { @@ -243,6 +245,9 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { l := len(sc.segmentTitles) - 1 // index starts at 0 if isNext { if sc.selectedIndex == l { + if !sc.IsAllowsCycle { + return + } sc.selectedIndex = 0 } else { sc.selectedIndex++ @@ -251,6 +256,9 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { sc.slideActionTitle.PushLeft() } else { if sc.selectedIndex == 0 { + if !sc.IsAllowsCycle { + return + } sc.selectedIndex = l } else { sc.selectedIndex-- diff --git a/ui/cryptomaterial/slide_action.go b/ui/cryptomaterial/slide_action.go index 16724aa69..5688b460a 100644 --- a/ui/cryptomaterial/slide_action.go +++ b/ui/cryptomaterial/slide_action.go @@ -109,6 +109,7 @@ func (s *SlideAction) DragLayout(gtx C, w layout.Widget) D { return dims } +// TransformLayout perform transition effects between 2 widgets func (s *SlideAction) TransformLayout(gtx C, w layout.Widget) D { if s.push != 0 { s.next = nil @@ -125,6 +126,7 @@ func (s *SlideAction) TransformLayout(gtx C, w layout.Widget) D { s.t0 = now } + // Calculate the duration of transition effects if s.offset != 0 { duration := s.Duration if duration == 0 { @@ -146,6 +148,7 @@ func (s *SlideAction) TransformLayout(gtx C, w layout.Widget) D { op.InvalidateOp{}.Add(gtx.Ops) } + // Record the widget presentation var dims layout.Dimensions { if s.next == nil { @@ -171,6 +174,7 @@ func (s *SlideAction) TransformLayout(gtx C, w layout.Widget) D { reverse = -1 } + // Implement transition effects for widgets if s.offset > 0 { defer op.Offset(image.Point{ X: int(float32(dims.Size.X)*(offset-1)) * reverse, diff --git a/ui/cryptomaterial/slider.go b/ui/cryptomaterial/slider.go index 3fe556864..d92ae4df2 100644 --- a/ui/cryptomaterial/slider.go +++ b/ui/cryptomaterial/slider.go @@ -93,6 +93,9 @@ func (s *Slider) Layout(gtx C, items []layout.Widget) D { return s.slideAction.TransformLayout(gtx, s.slideItems[s.selected].widgetItem) }), layout.Stacked(func(gtx C) D { + if len(s.slideItems) == 1 { + return D{} + } return layout.Inset{ Right: values.MarginPadding15, Left: values.MarginPadding15, From 2307611ee9a60f9252bb5f3e139de59f3b7cc39f Mon Sep 17 00:00:00 2001 From: Justin Do Date: Wed, 6 Dec 2023 11:02:58 +0700 Subject: [PATCH 12/13] update layout for slider on overview pag --- ui/cryptomaterial/segmented_control.go | 6 +++--- ui/page/root/overview_page.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index f47c2c4eb..1de9d01ac 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -36,7 +36,7 @@ type SegmentedControl struct { slideActionTitle *SlideAction segmentType SegmentType - IsAllowsCycle bool + allowCycle bool } func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType) *SegmentedControl { @@ -248,7 +248,7 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { l := len(sc.segmentTitles) - 1 // index starts at 0 if isNext { if sc.selectedIndex == l { - if !sc.IsAllowsCycle { + if !sc.allowCycle { return } sc.selectedIndex = 0 @@ -259,7 +259,7 @@ func (sc *SegmentedControl) handleActionEvent(isNext bool) { sc.slideActionTitle.PushLeft() } else { if sc.selectedIndex == 0 { - if !sc.IsAllowsCycle { + if !sc.allowCycle { return } sc.selectedIndex = l diff --git a/ui/page/root/overview_page.go b/ui/page/root/overview_page.go index 8c04fd94f..11f0a8947 100644 --- a/ui/page/root/overview_page.go +++ b/ui/page/root/overview_page.go @@ -406,7 +406,7 @@ func (pg *OverviewPage) assetBalanceItemLayout(item *assetBalanceSliderItem, row return item.backgroundImage.LayoutSize2(gtx, gtx.Metric.PxToDp(width), gtx.Metric.PxToDp(height)) }), layout.Expanded(func(gtx C) D { - return pg.contentSliderLayout(item)(gtx) + return layout.Center.Layout(gtx, pg.contentSliderLayout(item)) }), ) }) From 48dfa019a5a763fdaeac3ad7044cb807c45d6d1e Mon Sep 17 00:00:00 2001 From: Justin Do Date: Wed, 6 Dec 2023 11:25:55 +0700 Subject: [PATCH 13/13] update slide action on segmented control --- ui/cryptomaterial/segmented_control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/cryptomaterial/segmented_control.go b/ui/cryptomaterial/segmented_control.go index 1de9d01ac..453c7ce3d 100644 --- a/ui/cryptomaterial/segmented_control.go +++ b/ui/cryptomaterial/segmented_control.go @@ -64,7 +64,7 @@ func (t *Theme) SegmentedControl(segmentTitles []string, segmentType SegmentType sc.slideActionTitle.SetDragEffect(50) sc.slideActionTitle.Draged(func(dragDirection SwipeDirection) { - isNext := dragDirection == SwipeLeft + isNext := dragDirection == SwipeRight sc.handleActionEvent(isNext) })