diff --git a/ui/cryptomaterial/dropdown.go b/ui/cryptomaterial/dropdown.go index 41762f0fb..50d6d0c46 100644 --- a/ui/cryptomaterial/dropdown.go +++ b/ui/cryptomaterial/dropdown.go @@ -41,9 +41,6 @@ type DropDown struct { shadow *Shadow expandedViewAlignment layout.Direction - // extraDisplay is a custom display that will be added to the end of the - // expanded dropdown view if provided. - extraDisplay func(gtx C) D FontWeight font.Weight BorderWidth unit.Dp BorderColor *color.NRGBA @@ -63,6 +60,14 @@ type DropDownItem struct { // DisplayFn is an alternate display function that can be used to layout the // item instead of using the default item layout. DisplayFn func(gtx C) D + // Set to true for items that cannot be selected. + PreventSelection bool +} + +// DropDown is like DropdownWithCustomPos but uses default values for +// groupPosition, and dropdownInset parameters. +func (t *Theme) DropDown(items []DropDownItem, group uint, reversePos bool) *DropDown { + return t.DropdownWithCustomPos(items, group, 0, 0, reversePos) } // DropdownWithCustomPos returns a dropdown component. {groupPosition} parameter @@ -72,16 +77,6 @@ type DropDownItem struct { // parameter is the left inset for the dropdown if {reversePos} is false, else // it'll become the right inset for the dropdown. func (t *Theme) DropdownWithCustomPos(items []DropDownItem, group uint, groupPosition uint, dropdownInset int, reversePos bool) *DropDown { - return t.dropdown(items, group, groupPosition, dropdownInset, reversePos) -} - -// DropDown is like DropdownWithCustomPos but uses default values for -// groupPosition, and dropdownInset parameters. -func (t *Theme) DropDown(items []DropDownItem, group uint, reversePos bool) *DropDown { - return t.dropdown(items, group, 0, 0, reversePos) -} - -func (t *Theme) dropdown(items []DropDownItem, group uint, groupPosition uint, dropdownInset int, reversePos bool) *DropDown { d := &DropDown{ theme: t, expanded: false, @@ -141,19 +136,16 @@ func (d *DropDown) Len() int { return len(d.items) } -// SetExtraDisplay sets d.extraDisplay. The provided extraDisplay will be shown -// at the end of the expanded dropdown view. -func (d *DropDown) SetExtraDisplay(extraDisplay func(gtx C) D) { - d.extraDisplay = extraDisplay -} - func (d *DropDown) handleEvents() { if d.expanded { for i := range d.items { index := i - for d.items[index].clickable.Clicked() { - d.selectedIndex = index + item := d.items[index] + for item.clickable.Clicked() { d.expanded = false + if !item.PreventSelection { + d.selectedIndex = index + } break } } @@ -172,9 +164,14 @@ func (d *DropDown) Changed() bool { if d.expanded { for i := range d.items { index := i - for d.items[index].clickable.Clicked() { - d.selectedIndex = index + item := d.items[index] + for item.clickable.Clicked() { d.expanded = false + if item.PreventSelection { + return false + } + + d.selectedIndex = index return true } } @@ -183,99 +180,6 @@ func (d *DropDown) Changed() bool { return false } -func (d *DropDown) layoutActiveIcon(gtx C, index int) D { - var icon *Icon - if !d.expanded { - icon = NewIcon(d.dropdownIcon) - } else if index == d.selectedIndex { - icon = NewIcon(d.navigationIcon) - } - - if icon == nil { - return D{} // return early - } - - icon.Color = d.theme.Color.Gray1 - if d.expanded && d.SelectedItemIconColor != nil { - icon.Color = *d.SelectedItemIconColor - } - - return icon.Layout(gtx, values.MarginPadding20) -} - -func (d *DropDown) itemLayout(gtx C, index int, clickable *Clickable, item *DropDownItem, radius int, bodyLayout *LinearLayout) D { - padding := values.MarginPadding10 - if item.Icon != nil { - padding = values.MarginPadding8 - } - - return LinearLayout{ - Width: MatchParent, - Height: WrapContent, - Clickable: clickable, - Padding: layout.UniformInset(padding), - Border: Border{Radius: Radius(radius)}, - }.Layout(gtx, - layout.Rigid(func(gtx C) D { - if item.Icon == nil { - return D{} - } - - return item.Icon.Layout24dp(gtx) - }), - layout.Flexed(1, func(gtx C) D { - if item.DisplayFn != nil { - return item.DisplayFn(gtx) - } - - return bodyLayout.Layout2(gtx, func(gtx C) D { - lbl := d.theme.Body2(item.Text) - if !d.expanded && len(item.Text) > maxDropdownItemTextLen { - lbl.Text = item.Text[:maxDropdownItemTextLen-3 /* subtract space for the ellipsis */] + "..." - } - lbl.Font.Weight = d.FontWeight - return lbl.Layout(gtx) - }) - }), - layout.Rigid(func(gtx C) D { - return d.layoutActiveIcon(gtx, index) - }), - ) -} - -func (d *DropDown) selectedItemLayout(gtx C) D { - if len(d.items) == 0 { - if d.extraDisplay != nil { - return d.extraDisplay(gtx) - } - return D{} - } - - item := d.items[d.selectedIndex] - clickable := d.clickable - clickable.Hoverable = d.MakeSelectedItemHoverableAfterCollapse - bodyLayout := LinearLayout{ - Width: MatchParent, - Height: WrapContent, - Padding: layout.Inset{Right: values.MarginPadding5}, - Direction: d.SelectedItemDirectionAfterCollapse, - } - - return d.itemLayout(gtx, d.selectedIndex, clickable, &item, 8, &bodyLayout) -} - -func (d *DropDown) layoutExpandedItem(gtx C, itemIndex int) D { - item := d.items[itemIndex] - body := LinearLayout{ - Width: MatchParent, - Height: WrapContent, - Padding: layout.Inset{Right: values.MarginPadding5}, - Direction: layout.W, - } - - return d.itemLayout(gtx, itemIndex, item.clickable, &item, 8, &body) -} - // defaultDropdownWidth returns the default width for a dropdown depending on // it's position. func defaultDropdownWidth(reversePosition bool) unit.Dp { @@ -293,38 +197,11 @@ func (d *DropDown) Layout(gtx C) D { d.handleEvents() if d.StackBelowCollapsedLayout { - layoutContents := []layout.StackChild{layout.Expanded(func(gtx C) D { - expanded := d.expanded - d.expanded = false // enforce a collapsed layout display before creating the layout Dimensions and undo later. - display := d.collapsedLayout(gtx) - d.expanded = expanded - return display - })} - - if d.expanded { - layoutContents = append(layoutContents, layout.Expanded(func(gtx layout.Context) layout.Dimensions { - // Adding d.ExpandedLayoutInset.Top accounts for the the extra - // shift in vertical space set by d.ExpandedLayoutInset.Top to - // ensure the expanded view has enough space for its elements. - maxY := unit.Dp(len(d.items))*values.MarginPadding50 + d.ExpandedLayoutInset.Top - if d.extraDisplay != nil { - maxY += values.MarginPadding50 - } - - gtx.Constraints.Max.Y = gtx.Dp(maxY) - return d.expandedLayout(gtx) - })) - } - - return layout.Stack{Alignment: d.expandedViewAlignment}.Layout(gtx, layoutContents...) + return d.stackExpandedViewBelowCollapsedView(gtx) } if d.GroupPosition == DropdownBasePos && d.isDropdownGroupCollapsed(d.group) { maxY := unit.Dp(len(d.items)) * values.MarginPadding50 - if d.extraDisplay != nil { - maxY += values.MarginPadding50 - } - gtx.Constraints.Max.Y = gtx.Dp(maxY) if d.expanded { return d.backdrop.Layout(gtx, func(gtx C) D { @@ -342,25 +219,52 @@ func (d *DropDown) Layout(gtx C) D { return layout.Stack{Alignment: d.expandedViewAlignment}.Layout(gtx, layout.Stacked(d.collapsedLayout)) } +// stackExpandedViewBelowCollapsedView stacks the expanded view right below the +// collapsed view such that both the current selection and the list of items are +// visible. +func (d *DropDown) stackExpandedViewBelowCollapsedView(gtx C) D { + layoutContents := []layout.StackChild{layout.Expanded(func(gtx C) D { + expanded := d.expanded + d.expanded = false // enforce a collapsed layout display before creating the layout Dimensions and undo later. + display := d.collapsedLayout(gtx) + d.expanded = expanded + return display + })} + + // No need to display expanded view when there is only one item. + if d.expanded && len(d.items) > 1 { + layoutContents = append(layoutContents, layout.Expanded(func(gtx layout.Context) layout.Dimensions { + // Adding d.ExpandedLayoutInset.Top accounts for the the extra + // shift in vertical space set by d.ExpandedLayoutInset.Top to + // ensure the expanded view has enough space for its elements. + maxY := unit.Dp(len(d.items))*values.MarginPadding50 + d.ExpandedLayoutInset.Top + gtx.Constraints.Max.Y = gtx.Dp(maxY) + return d.expandedLayout(gtx) + })) + } + + return layout.Stack{Alignment: d.expandedViewAlignment}.Layout(gtx, layoutContents...) +} + // expandedLayout computes dropdown layout when dropdown is opened. func (d *DropDown) expandedLayout(gtx C) D { - listItems := len(d.items) return d.ExpandedLayoutInset.Layout(gtx, func(gtx C) D { return d.drawLayout(gtx, func(gtx C) D { list := &layout.List{Axis: layout.Vertical} - return list.Layout(gtx, listItems, func(gtx C, index int) D { - if (index == listItems-1 || listItems == 0) && d.extraDisplay != nil { - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx C) D { - return d.layoutExpandedItem(gtx, index) - }), - layout.Rigid(d.extraDisplay), - ) - } else if listItems != 0 { - return d.layoutExpandedItem(gtx, index) + return list.Layout(gtx, len(d.items), func(gtx C, index int) D { + if len(d.items) == 0 { + return D{} } - return D{} + item := d.items[index] + body := LinearLayout{ + Width: MatchParent, + Height: WrapContent, + Padding: layout.Inset{Right: values.MarginPadding5}, + Direction: layout.W, + } + + return d.itemLayout(gtx, index, item.clickable, &item, 8, &body) }) }) }) @@ -369,10 +273,90 @@ func (d *DropDown) expandedLayout(gtx C) D { // collapsedLayout computes dropdown layout when dropdown is closed. func (d *DropDown) collapsedLayout(gtx C) D { return d.collapsedLayoutInset.Layout(gtx, func(gtx C) D { - return d.drawLayout(gtx, d.selectedItemLayout) + return d.drawLayout(gtx, func(gtx C) D { + if len(d.items) == 0 { + return D{} + } + + selectedItem := d.items[d.selectedIndex] + bodyLayout := LinearLayout{ + Width: MatchParent, + Height: WrapContent, + Padding: layout.Inset{Right: values.MarginPadding5}, + Direction: d.SelectedItemDirectionAfterCollapse, + } + + // d.MakeSelectedItemHoverableAfterCollapse is set after creating + // the dropdown but before drawing the layout. + d.clickable.Hoverable = d.MakeSelectedItemHoverableAfterCollapse + return d.itemLayout(gtx, d.selectedIndex, d.clickable, &selectedItem, 8, &bodyLayout) + }) }) } +func (d *DropDown) itemLayout(gtx C, index int, clickable *Clickable, item *DropDownItem, radius int, bodyLayout *LinearLayout) D { + padding := values.MarginPadding10 + if item.Icon != nil { + padding = values.MarginPadding8 + } + + return LinearLayout{ + Width: MatchParent, + Height: WrapContent, + Clickable: clickable, + Padding: layout.UniformInset(padding), + Border: Border{Radius: Radius(radius)}, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + if item.Icon == nil { + return D{} + } + + return item.Icon.Layout24dp(gtx) + }), + layout.Flexed(1, func(gtx C) D { + if item.DisplayFn != nil { + return item.DisplayFn(gtx) + } + + return bodyLayout.Layout2(gtx, func(gtx C) D { + lbl := d.theme.Body2(item.Text) + if !d.expanded && len(item.Text) > maxDropdownItemTextLen { + lbl.Text = item.Text[:maxDropdownItemTextLen-3 /* subtract space for the ellipsis */] + "..." + } + lbl.Font.Weight = d.FontWeight + return lbl.Layout(gtx) + }) + }), + layout.Rigid(func(gtx C) D { + if !item.PreventSelection && len(d.items) > 1 { + return d.layoutActiveIcon(gtx, index) + } + return D{} + }), + ) +} + +func (d *DropDown) layoutActiveIcon(gtx C, index int) D { + var icon *Icon + if !d.expanded { + icon = NewIcon(d.dropdownIcon) + } else if index == d.selectedIndex { + icon = NewIcon(d.navigationIcon) + } + + if icon == nil { + return D{} // return early + } + + icon.Color = d.theme.Color.Gray1 + if d.expanded && d.SelectedItemIconColor != nil { + icon.Color = *d.SelectedItemIconColor + } + + return icon.Layout(gtx, values.MarginPadding20) +} + // drawLayout wraps the page tx and sync section in a card layout func (d *DropDown) drawLayout(gtx C, body layout.Widget) D { if d.Width <= 0 { diff --git a/ui/page/dcrdex/dex_onboarding_page.go b/ui/page/dcrdex/dex_onboarding_page.go index 7a95b93d8..f7429d95a 100644 --- a/ui/page/dcrdex/dex_onboarding_page.go +++ b/ui/page/dcrdex/dex_onboarding_page.go @@ -43,10 +43,10 @@ var ( // formWidth is the width for form elements on the onboarding DEX page. formWidth = values.MarginPadding450 - u20 = values.MarginPadding20 - u16 = values.MarginPadding16 - u2 = values.MarginPadding2 - u10 = values.MarginPadding10 + dp20 = values.MarginPadding20 + dp16 = values.MarginPadding16 + dp2 = values.MarginPadding2 + dp10 = values.MarginPadding10 ) // onboardingStep is each step of the flow required for a user to create a DEX @@ -193,8 +193,8 @@ func (pg *DEXOnboarding) Layout(gtx C) D { Orientation: layout.Vertical, Background: pg.Theme.Color.Surface, Margin: layout.Inset{ - Right: u20, - Left: u20, + Right: dp20, + Left: dp20, }, Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(r), @@ -204,7 +204,7 @@ func (pg *DEXOnboarding) Layout(gtx C) D { layout.Rigid(func(gtx C) D { txt := pg.Theme.Body1(values.String(values.StrDCRDEXWelcomeMessage)) txt.Font.Weight = font.Bold - return pg.centerLayout(gtx, u16, u20, txt.Layout) + return pg.centerLayout(gtx, dp16, dp20, txt.Layout) }), layout.Rigid(func(gtx C) D { return pg.onBoardingStepRow(gtx) @@ -305,12 +305,12 @@ func (pg *DEXOnboarding) onBoardingStep(gtx C, step onboardingStep, stepDesc str lb := pg.Theme.Label(values.TextSize16, fmt.Sprintf("%d", step)) lb.Color = textColor lb.Font.Weight = font.SemiBold - return layout.Inset{Top: u10, Bottom: u10}.Layout(gtx, lb.Layout) + return layout.Inset{Top: dp10, Bottom: dp10}.Layout(gtx, lb.Layout) }), ) }), layout.Rigid(func(gtx C) D { - inset := layout.Inset{Top: u10, Bottom: u10} + inset := layout.Inset{Top: dp10, Bottom: dp10} if !activeStep { return inset.Layout(gtx, semiBoldLabelGrey3(pg.Theme, stepDesc).Layout) } @@ -334,10 +334,10 @@ func (pg *DEXOnboarding) stepSetPassword(gtx C) D { return pg.centerLayout(gtx, 0, 0, pg.Theme.Body1(values.String(values.StrSetTradePasswordDesc)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.passwordEditor.Layout) + return layout.Inset{Top: dp16}.Layout(gtx, pg.passwordEditor.Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.confirmPasswordEditor.Layout) + return layout.Inset{Top: dp16}.Layout(gtx, pg.confirmPasswordEditor.Layout) }), layout.Rigid(func(gtx C) D { return pg.formFooterButtons(gtx) @@ -351,7 +351,7 @@ func (pg *DEXOnboarding) stepSetPassword(gtx C) D { func (pg *DEXOnboarding) stepChooseServer(gtx C) D { layoutFlex := layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, u20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrSelectServer)).Layout) + return pg.centerLayout(gtx, dp20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrSelectServer)).Layout) }), layout.Rigid(func(gtx C) D { return pg.centerLayout(gtx, 0, 0, pg.Theme.Body1(values.String(values.StrSelectDEXServerDesc)).Layout) @@ -359,11 +359,11 @@ func (pg *DEXOnboarding) stepChooseServer(gtx C) D { layout.Rigid(func(gtx C) D { l := pg.Theme.Label(values.TextSize16, values.String(values.StrServer)) l.Font.Weight = font.Bold - return layout.Inset{Top: u20}.Layout(gtx, l.Layout) + return layout.Inset{Top: dp20}.Layout(gtx, l.Layout) }), layout.Rigid(pg.serverDropDown.Layout), layout.Rigid(components.IconButton(pg.Theme.Icons.ContentAdd, values.String(values.StrAddServer), - layout.Inset{Top: u16}, pg.Theme, pg.addServerBtn), + layout.Inset{Top: dp16}, pg.Theme, pg.addServerBtn), ), layout.Rigid(func(gtx C) D { return pg.formFooterButtons(gtx) @@ -385,7 +385,7 @@ func (pg *DEXOnboarding) subStepAddServer(gtx C) D { Width: width, Height: cryptomaterial.WrapContent, Orientation: layout.Horizontal, - Margin: layout.Inset{Top: values.MarginPadding20, Bottom: u16}, + Margin: layout.Inset{Top: values.MarginPadding20, Bottom: dp16}, Alignment: layout.Middle, }.Layout(gtx, layout.Rigid(func(gtx C) D { @@ -415,10 +415,10 @@ func (pg *DEXOnboarding) subStepAddServer(gtx C) D { return pg.centerLayout(gtx, 0, 0, pg.Theme.Body1(values.String(values.StrAddServerDesc)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.serverURLEditor.Layout) + return layout.Inset{Top: dp16}.Layout(gtx, pg.serverURLEditor.Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.serverCertEditor.Layout) + return layout.Inset{Top: dp16}.Layout(gtx, pg.serverCertEditor.Layout) }), layout.Rigid(func(gtx C) D { return pg.formFooterButtons(gtx) @@ -478,16 +478,16 @@ func (pg *DEXOnboarding) formFooterButtons(gtx C) D { func (pg *DEXOnboarding) stepPostBond(gtx C) D { layoutFlex := layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, u20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrPostBond)).Layout) + return pg.centerLayout(gtx, dp20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrPostBond)).Layout) }), layout.Rigid(func(gtx C) D { return pg.centerLayout(gtx, 0, 0, pg.Theme.Body1(values.String(values.StrSelectBondWalletMsg)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrSupportedWallets)).Layout) + return layout.Inset{Top: dp20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrSupportedWallets)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u2}.Layout(gtx, func(gtx C) D { + return layout.Inset{Top: dp2}.Layout(gtx, func(gtx C) D { if pg.bondSourceWalletSelector == nil { return D{} // TODO: return btn to create wallet } @@ -495,10 +495,10 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { }) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrAccount)).Layout) + return layout.Inset{Top: dp20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrAccount)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u2}.Layout(gtx, func(gtx C) D { + return layout.Inset{Top: dp2}.Layout(gtx, func(gtx C) D { if pg.bondSourceAccountSelector == nil { return D{} } @@ -506,13 +506,13 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { }) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u20 * u2, Bottom: u20 * u2}.Layout(gtx, pg.Theme.Separator().Layout) + return layout.Inset{Top: dp20 * dp2, Bottom: dp20 * dp2}.Layout(gtx, pg.Theme.Separator().Layout) }), layout.Rigid(func(gtx C) D { return layout.S.Layout(gtx, pg.Theme.Body1(values.String(values.StrSelectBondStrengthMsg)).Layout) }), layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, u20, u16, renderers.RenderHTML(values.String(values.StrPostBondDesc), pg.Theme).Layout) + return pg.centerLayout(gtx, dp20, dp16, renderers.RenderHTML(values.String(values.StrPostBondDesc), pg.Theme).Layout) }), layout.Rigid(func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, @@ -531,7 +531,7 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { layout.Flexed(0.5, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16, Right: u10}.Layout(gtx, func(gtx C) D { + return layout.Inset{Top: dp16, Right: dp10}.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(pg.semiBoldLabel(values.String(values.StrBondStrength)).Layout), layout.Rigid(func(gtx C) D { @@ -539,24 +539,24 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { Width: cryptomaterial.WrapContent, Height: cryptomaterial.WrapContent, Clickable: pg.bondStrengthMoreInfo, - Padding: layout.Inset{Top: u2, Left: u2}, + Padding: layout.Inset{Top: dp2, Left: dp2}, }.Layout2(gtx, pg.Theme.Icons.InfoAction.Layout16dp) }), ) }) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Right: u10}.Layout(gtx, pg.bondStrengthEditor.Layout) + return layout.Inset{Right: dp10}.Layout(gtx, pg.bondStrengthEditor.Layout) }), ) }), layout.Flexed(0.5, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16, Left: u10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrNewTier)).Layout) + return layout.Inset{Top: dp16, Left: dp10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrNewTier)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Left: u10}.Layout(gtx, pg.viewOnlyCard(nil, func(gtx C) D { + return layout.Inset{Left: dp10}.Layout(gtx, pg.viewOnlyCard(nil, func(gtx C) D { return pg.Theme.Label(values.TextSize16, fmt.Sprintf("%d", pg.newTier)).Layout(gtx) })) }), @@ -569,7 +569,7 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { layout.Flexed(0.3, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrCurrency)).Layout) + return layout.Inset{Top: dp16}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrCurrency)).Layout) }), layout.Rigid(func(gtx C) D { return pg.viewOnlyCard(&pg.Theme.Color.Gray2, func(gtx C) D { @@ -592,10 +592,10 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { layout.Flexed(0.7, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16, Left: u10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrTotalCost)).Layout) + return layout.Inset{Top: dp16, Left: dp10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrTotalCost)).Layout) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Left: u10}.Layout(gtx, pg.viewOnlyCard(nil, func(gtx C) D { + return layout.Inset{Left: dp10}.Layout(gtx, pg.viewOnlyCard(nil, func(gtx C) D { return pg.bondAmountInfoDisplay(gtx) })) }), @@ -604,7 +604,7 @@ func (pg *DEXOnboarding) stepPostBond(gtx C) D { ) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u16}.Layout(gtx, pg.formFooterButtons) + return layout.Inset{Top: dp16}.Layout(gtx, pg.formFooterButtons) }), ) @@ -632,12 +632,12 @@ func (pg *DEXOnboarding) viewOnlyCard(bg *color.NRGBA, info func(gtx C) D) func( Orientation: layout.Vertical, Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), - Width: u2, + Width: dp2, Color: pg.Theme.Color.Gray2, }, Margin: layout.Inset{ - Top: u2, - Bottom: u2, + Top: dp2, + Bottom: dp2, }, Padding: layout.Inset{ Top: u12, @@ -655,7 +655,7 @@ func (pg *DEXOnboarding) stepWaitForBondConfirmation(gtx C) D { gtx.Constraints.Max.X = gtx.Dp(width) layoutFlex := layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.centerLayout(gtx, u20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrPostBond)).Layout) + return pg.centerLayout(gtx, dp20, values.MarginPadding12, pg.Theme.H6(values.String(values.StrPostBond)).Layout) }), layout.Rigid(func(gtx C) D { return pg.centerLayout(gtx, 0, 0, renderers.RenderHTML(values.String(values.StrPostBondDesc), pg.Theme).Layout) @@ -667,26 +667,26 @@ func (pg *DEXOnboarding) stepWaitForBondConfirmation(gtx C) D { Background: pg.Theme.Color.Gray4, Orientation: layout.Vertical, Margin: layout.Inset{ - Top: u20, + Top: dp20, Bottom: u30, }, Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), }, - Padding: layout.UniformInset(u16), + Padding: layout.UniformInset(dp16), }.Layout(gtx, layout.Rigid(func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u2}.Layout(gtx, pg.Theme.Icons.TimerIcon.Layout16dp) + return layout.Inset{Top: dp2}.Layout(gtx, pg.Theme.Icons.TimerIcon.Layout16dp) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Left: u10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrWaitingForConfirmation)).Layout) + return layout.Inset{Left: dp10}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrWaitingForConfirmation)).Layout) }), ) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: 10, Bottom: u10}.Layout(gtx, pg.Theme.Body1(values.StringF(values.StrDEXBondConfirmationMsg, "dex.decred.org", 2 /* TODO: use real values */)).Layout) + return layout.Inset{Top: 10, Bottom: dp10}.Layout(gtx, pg.Theme.Body1(values.StringF(values.StrDEXBondConfirmationMsg, "dex.decred.org", 2 /* TODO: use real values */)).Layout) }), layout.Rigid(func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, @@ -701,7 +701,7 @@ func (pg *DEXOnboarding) stepWaitForBondConfirmation(gtx C) D { ) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Bottom: u20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrPaymentDetails)).Layout) + return layout.Inset{Bottom: dp20}.Layout(gtx, pg.semiBoldLabel(values.String(values.StrPaymentDetails)).Layout) }), layout.Rigid(func(gtx C) D { return layout.Inset{Bottom: values.MarginPadding60}.Layout(gtx, func(gtx C) D { diff --git a/ui/page/dcrdex/market.go b/ui/page/dcrdex/market.go index 79351d250..e8c8402bf 100644 --- a/ui/page/dcrdex/market.go +++ b/ui/page/dcrdex/market.go @@ -28,9 +28,9 @@ const ( ) var ( - u5 = values.MarginPadding5 - u8 = values.MarginPadding8 - u300 = values.MarginPadding300 + dp5 = values.MarginPadding5 + dp8 = values.MarginPadding8 + dp300 = values.DP300 orderFormAndOrderBookWidth = (values.AppWidth / 2) - 40 // Minus 40 px to allow for margin between the order form and order book. // orderFormAndOrderBookHeight is a an arbitrary height that accommodates // the current order form elements and maxOrderDisplayedInOrderBook. Use @@ -118,7 +118,7 @@ func NewDEXMarketPage(l *load.Load) *DEXMarketPage { ordersTableHorizontalScroll: &widget.List{List: layout.List{Axis: horizontal, Alignment: layout.Middle}}, } - btnPadding := layout.Inset{Top: u8, Right: u20, Left: u20, Bottom: u8} + btnPadding := layout.Inset{Top: dp8, Right: dp20, Left: dp20, Bottom: dp8} pg.toggleBuyAndSellBtn.Padding = btnPadding pg.openOrdersBtn.Inset, pg.orderHistoryBtn.Inset = btnPadding, btnPadding pg.openOrdersBtn.Font.Weight, pg.orderHistoryBtn.Font.Weight = font.SemiBold, font.SemiBold @@ -161,8 +161,13 @@ func (pg *DEXMarketPage) OnNavigatedTo() { }) } + // Include the "Add Server" button. + servers = append(servers, cryptomaterial.DropDownItem{ + DisplayFn: pg.addServerBtnDisplay, + PreventSelection: true, + }) + pg.serverSelector = pg.Theme.DropDown(servers, values.DEXServerDropdownGroup, false) - pg.serverSelector.SetExtraDisplay(pg.addServerBtnDisplay) pg.marketSelector = pg.Theme.DropDown([]cryptomaterial.DropDownItem{ { Text: "DCR/BTC", @@ -178,9 +183,9 @@ func (pg *DEXMarketPage) OnNavigatedTo() { }, }, values.DEXCurrencyPairGroup, false) - pg.serverSelector.Width, pg.marketSelector.Width = u300, u300 + pg.serverSelector.Width, pg.marketSelector.Width = dp300, dp300 pg.serverSelector.StackBelowCollapsedLayout, pg.marketSelector.StackBelowCollapsedLayout = true, true - pg.serverSelector.BorderWidth, pg.marketSelector.BorderWidth = u2, u2 + pg.serverSelector.BorderWidth, pg.marketSelector.BorderWidth = dp2, dp2 pg.serverSelector.MakeSelectedItemHoverableAfterCollapse, pg.marketSelector.MakeSelectedItemHoverableAfterCollapse = false, false pg.serverSelector.SelectedItemIconColor, pg.marketSelector.SelectedItemIconColor = &pg.Theme.Color.Primary, &pg.Theme.Color.Primary @@ -203,12 +208,12 @@ func (pg *DEXMarketPage) marketDropdownListItem(baseAsset, quoteAsset libutils.A return baseIcon.Layout20dp(gtx) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Right: u2, Left: u2}.Layout(gtx, pg.Theme.Label(values.TextSize16, baseAsset.String()).Layout) + return layout.Inset{Right: dp2, Left: dp2}.Layout(gtx, pg.Theme.Label(values.TextSize16, baseAsset.String()).Layout) }), ) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Right: u2, Left: u2}.Layout(gtx, pg.Theme.Label(values.TextSize16, "/").Layout) + return layout.Inset{Right: dp2, Left: dp2}.Layout(gtx, pg.Theme.Label(values.TextSize16, "/").Layout) }), layout.Rigid(func(gtx C) D { return layout.Flex{Axis: horizontal}.Layout(gtx, @@ -219,7 +224,7 @@ func (pg *DEXMarketPage) marketDropdownListItem(baseAsset, quoteAsset libutils.A return quoteIcon.Layout20dp(gtx) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Right: u2, Left: u2}.Layout(gtx, pg.Theme.Label(values.TextSize16, quoteAsset.String()).Layout) + return layout.Inset{Right: dp2, Left: dp2}.Layout(gtx, pg.Theme.Label(values.TextSize16, quoteAsset.String()).Layout) }), ) }), @@ -263,8 +268,8 @@ func (pg *DEXMarketPage) Layout(gtx C) D { Height: cryptomaterial.MatchParent, Margin: layout.Inset{ Bottom: values.MarginPadding30, - Right: u10, - Left: u10, + Right: dp10, + Left: dp10, }, Direction: layout.Center, }.Layout2(gtx, func(gtx C) D { @@ -287,19 +292,15 @@ func (pg *DEXMarketPage) Layout(gtx C) D { // addServerBtnDisplay returns a clickable button layout. The pg.addServerBtn // will should open a modal or page to add a new server to DEX when clicked. func (pg DEXMarketPage) addServerBtnDisplay(gtx C) D { - return layout.Flex{Axis: vertical}.Layout(gtx, - layout.Rigid(pg.Theme.Separator().Layout), - layout.Rigid(components.IconButton(pg.Theme.Icons.ContentAdd, values.String(values.StrAddServer), - layout.Inset{Top: u10, Bottom: u5, Left: values.MarginPadding8}, pg.Theme, pg.addServerBtn, - )), - ) + return components.IconButton(pg.Theme.Icons.ContentAdd, values.String(values.StrAddServer), layout.Inset{}, pg.Theme, pg.addServerBtn)(gtx) } + func (pg *DEXMarketPage) serverAndCurrencySelection(gtx C) D { return cryptomaterial.LinearLayout{ Width: cryptomaterial.MatchParent, Height: gtx.Dp(100), Background: pg.Theme.Color.Surface, - Padding: layout.UniformInset(u16), + Padding: layout.UniformInset(dp16), Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), }, @@ -310,7 +311,7 @@ func (pg *DEXMarketPage) serverAndCurrencySelection(gtx C) D { layout.Rigid(func(gtx C) D { pg.serverSelector.Background = &pg.Theme.Color.Surface pg.serverSelector.BorderColor = &pg.Theme.Color.Gray5 - return layout.Inset{Top: u2}.Layout(gtx, pg.serverSelector.Layout) + return layout.Inset{Top: dp2}.Layout(gtx, pg.serverSelector.Layout) }), ) }), @@ -321,7 +322,7 @@ func (pg *DEXMarketPage) serverAndCurrencySelection(gtx C) D { layout.Rigid(func(gtx C) D { pg.marketSelector.Background = &pg.Theme.Color.Surface pg.marketSelector.BorderColor = &pg.Theme.Color.Gray5 - return layout.Inset{Top: u2}.Layout(gtx, pg.marketSelector.Layout) + return layout.Inset{Top: dp2}.Layout(gtx, pg.marketSelector.Layout) }), ) }) @@ -344,7 +345,7 @@ func (pg *DEXMarketPage) priceAndVolumeDetail(gtx C) D { Width: cryptomaterial.MatchParent, Height: cryptomaterial.WrapContent, Padding: layout.UniformInset(16), - Margin: layout.Inset{Top: u5, Bottom: u5}, + Margin: layout.Inset{Top: dp5, Bottom: dp5}, Background: pg.Theme.Color.Surface, Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), @@ -394,7 +395,7 @@ func (pg *DEXMarketPage) priceAndVolumeColume(gtx C, title1 string, body1 func(g return cryptomaterial.LinearLayout{ Width: cryptomaterial.WrapContent, Height: cryptomaterial.WrapContent, - Margin: layout.Inset{Bottom: u20}, + Margin: layout.Inset{Bottom: dp20}, Orientation: vertical, }.Layout(gtx, layout.Rigid(semiBoldLabelGrey3(pg.Theme, title1).Layout), @@ -436,8 +437,8 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { Width: gtx.Dp(orderFormAndOrderBookWidth), Height: gtx.Dp(orderFormAndOrderBookHeight), Background: pg.Theme.Color.Surface, - Margin: layout.Inset{Top: u5, Bottom: u5}, - Padding: layout.UniformInset(u16), + Margin: layout.Inset{Top: dp5, Bottom: dp5}, + Padding: layout.UniformInset(dp16), Direction: layout.E, Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), @@ -461,7 +462,7 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { layout.Rigid(func(gtx C) D { return orderFormRow(gtx, vertical, []layout.FlexChild{ layout.Rigid(func(gtx C) D { - return layout.Inset{Bottom: u5}.Layout(gtx, func(gtx C) D { + return layout.Inset{Bottom: dp5}.Layout(gtx, func(gtx C) D { var labelText string if pg.switchLotsOrAmount.IsChecked() { labelText = values.String(values.StrAmount) @@ -502,7 +503,7 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { layout.Rigid(func(gtx C) D { return orderFormRow(gtx, vertical, []layout.FlexChild{ layout.Rigid(func(gtx C) D { - return layout.Inset{Bottom: u5}.Layout(gtx, pg.semiBoldLabelText(values.String(values.StrTotal)).Layout) + return layout.Inset{Bottom: dp5}.Layout(gtx, pg.semiBoldLabelText(values.String(values.StrTotal)).Layout) }), layout.Rigid(pg.totalEditor.Layout), }) @@ -517,7 +518,7 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { return orderFormRow(gtx, horizontal, []layout.FlexChild{ layout.Rigid(pg.immediateOrder.Layout), layout.Rigid(func(gtx C) D { - return components.ImageIconWithClickable(gtx, layout.Inset{Top: u10, Left: u2}, pg.immediateMoreInfo, pg.Theme.Icons.InfoAction.Layout16dp) + return components.ImageIconWithClickable(gtx, layout.Inset{Top: dp10, Left: dp2}, pg.immediateMoreInfo, pg.Theme.Icons.InfoAction.Layout16dp) })}, ) }), @@ -533,7 +534,7 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { layout.Rigid(pg.toggleBuyAndSellBtn.Layout), layout.Flexed(1, func(gtx C) D { pg.orderTypesDropdown.Background = &pg.Theme.Color.Surface - return layout.Inset{Bottom: u5, Top: u5}.Layout(gtx, pg.orderTypesDropdown.Layout) + return layout.Inset{Bottom: dp5, Top: dp5}.Layout(gtx, pg.orderTypesDropdown.Layout) }), ) }), @@ -545,7 +546,7 @@ func orderFormRow(gtx C, orientation layout.Axis, children []layout.FlexChild) D return cryptomaterial.LinearLayout{ Width: cryptomaterial.MatchParent, Height: cryptomaterial.WrapContent, - Margin: layout.Inset{Bottom: u10, Top: u10}, + Margin: layout.Inset{Bottom: dp10, Top: dp10}, Orientation: orientation, }.Layout(gtx, children...) } @@ -577,8 +578,8 @@ func (pg *DEXMarketPage) orderbook(gtx C) D { Width: gtx.Dp(orderFormAndOrderBookWidth), Height: gtx.Dp(orderFormAndOrderBookHeight), Background: pg.Theme.Color.Surface, - Margin: layout.Inset{Top: u5, Bottom: u5}, - Padding: layout.UniformInset(u16), + Margin: layout.Inset{Top: dp5, Bottom: dp5}, + Padding: layout.UniformInset(dp16), Border: cryptomaterial.Border{Radius: cryptomaterial.Radius(8)}, Orientation: vertical, Direction: layout.Center, @@ -592,7 +593,7 @@ func (pg *DEXMarketPage) orderbook(gtx C) D { ) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u10}.Layout(gtx, func(gtx C) D { + return layout.Inset{Top: dp10}.Layout(gtx, func(gtx C) D { return pg.orderBookRow(gtx, semiBoldGray3Size14(pg.Theme, values.StringF(values.StrAssetPrice, "BTC")), semiBoldGray3Size14(pg.Theme, values.StringF(values.StrAssetAmount, "DCR")), @@ -604,7 +605,7 @@ func (pg *DEXMarketPage) orderbook(gtx C) D { return layout.Flex{Axis: vertical}.Layout(gtx, makeOrderBookBuyOrSell(true)...) }), layout.Rigid(func(gtx C) D { - return layout.Inset{Top: u5, Bottom: u10}.Layout(gtx, func(gtx C) D { + return layout.Inset{Top: dp5, Bottom: dp10}.Layout(gtx, func(gtx C) D { return layout.Stack{Alignment: layout.Center}.Layout(gtx, layout.Stacked(pg.Theme.Separator().Layout), layout.Expanded(func(gtx C) D { @@ -612,7 +613,7 @@ func (pg *DEXMarketPage) orderbook(gtx C) D { Width: cryptomaterial.WrapContent, Height: cryptomaterial.WrapContent, Background: pg.Theme.Color.Gray3, - Padding: layout.Inset{Top: u5, Bottom: u5, Right: u16, Left: u16}, + Padding: layout.Inset{Top: dp5, Bottom: dp5, Right: dp16, Left: dp16}, Border: cryptomaterial.Border{Radius: cryptomaterial.Radius(16)}, Direction: layout.Center, Orientation: horizontal, @@ -719,8 +720,8 @@ func (pg *DEXMarketPage) openOrdersAndHistory(gtx C) D { Width: cryptomaterial.MatchParent, Height: gtx.Dp(400), Background: pg.Theme.Color.Surface, - Margin: layout.Inset{Top: u5, Bottom: 30}, - Padding: layout.UniformInset(u10), + Margin: layout.Inset{Top: dp5, Bottom: 30}, + Padding: layout.UniformInset(dp10), Border: cryptomaterial.Border{ Radius: cryptomaterial.Radius(8), }, @@ -742,7 +743,7 @@ func (pg *DEXMarketPage) openOrdersAndHistory(gtx C) D { } return layout.Flex{Axis: horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Left: u5, Right: u10}.Layout(gtx, pg.openOrdersBtn.Layout) + return layout.Inset{Left: dp5, Right: dp10}.Layout(gtx, pg.openOrdersBtn.Layout) }), layout.Rigid(pg.orderHistoryBtn.Layout), ) @@ -821,7 +822,7 @@ func (pg *DEXMarketPage) orderColumn(header bool, txt string, columnWidth unit.D Height: cryptomaterial.WrapContent, Orientation: horizontal, Alignment: layout.Middle, - Padding: layout.Inset{Top: u16, Bottom: u16}, + Padding: layout.Inset{Top: dp16, Bottom: dp16}, Direction: layout.Center, }.Layout2(gtx, func(gtx C) D { if header { diff --git a/ui/values/dimensions.go b/ui/values/dimensions.go index 6380b6738..3cef8673c 100644 --- a/ui/values/dimensions.go +++ b/ui/values/dimensions.go @@ -84,7 +84,7 @@ var ( MarginPadding250 = unit.Dp(250) MarginPaddingMinus230 = unit.Dp(-230) MarginPadding280 = unit.Dp(280) - MarginPadding300 = unit.Dp(300) + DP300 = unit.Dp(300) MarginPadding350 = unit.Dp(350) MarginPadding368 = unit.Dp(368) MarginPadding372 = unit.Dp(372)