fix(context-menu): theme popover chrome
- commit
- 50d6c6211263404dea148fb190fbfd2133ebf9d9
- author
- brice · · 2026-04-14 05:39 UTC
- committer
- brice · · 2026-04-14 12:06 UTC
- parent
- 5b526c7
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +152 -2
M internal/infrastructure/gtkmenu/renderer.go +29 -2
| @@ -11,6 +11,13 @@ type renderItem struct { | |||
| 11 | 11 | separator bool | |
| 12 | 12 | } | |
| 13 | 13 | ||
| 14 | + | const ( | |
| 15 | + | contextMenuBoxClass = "context-menu" | |
| 16 | + | contextMenuPopoverClass = "context-menu-popover" | |
| 17 | + | contextMenuItemClass = "context-menu-item" | |
| 18 | + | contextMenuSeparatorClass = "context-menu-separator" | |
| 19 | + | ) | |
| 20 | + | ||
| 14 | 21 | // Renderer shows GTK context menus on the provided anchor widget. | |
| 15 | 22 | type Renderer struct { | |
| 16 | 23 | dispatch func(func()) | |
| @@ -33,6 +40,18 @@ func BuildButtons(items []port.MenuItem) []renderItem { | |||
| 33 | 40 | return renderItems | |
| 34 | 41 | } | |
| 35 | 42 | ||
| 43 | + | func contextMenuItemClasses() []string { | |
| 44 | + | return []string{"flat", contextMenuItemClass} | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | func contextMenuPopoverClasses() []string { | |
| 48 | + | return []string{contextMenuPopoverClass} | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | func contextMenuSeparatorClasses() []string { | |
| 52 | + | return []string{contextMenuSeparatorClass} | |
| 53 | + | } | |
| 54 | + | ||
| 36 | 55 | func isSeparator(item port.MenuItem) bool { | |
| 37 | 56 | return item.Action == "" && item.Label == "" | |
| 38 | 57 | } | |
| @@ -79,7 +98,7 @@ func showMenu( | |||
| 79 | 98 | } | |
| 80 | 99 | ||
| 81 | 100 | box := gtk.NewBox(gtk.OrientationVerticalValue, 0) | |
| 82 | - | box.AddCssClass("context-menu") | |
| 101 | + | box.AddCssClass(contextMenuBoxClass) | |
| 83 | 102 | ||
| 84 | 103 | selected := false | |
| 85 | 104 | var popover *gtk.Popover | |
| @@ -89,13 +108,18 @@ func showMenu( | |||
| 89 | 108 | for _, item := range items { | |
| 90 | 109 | if item.separator { | |
| 91 | 110 | sep := gtk.NewSeparator(gtk.OrientationHorizontalValue) | |
| 111 | + | for _, class := range contextMenuSeparatorClasses() { | |
| 112 | + | sep.AddCssClass(class) | |
| 113 | + | } | |
| 92 | 114 | box.Append(&sep.Widget) | |
| 93 | 115 | continue | |
| 94 | 116 | } | |
| 95 | 117 | ||
| 96 | 118 | btn := gtk.NewButton() | |
| 97 | 119 | btn.SetLabel(item.item.Label) | |
| 98 | - | btn.AddCssClass("flat") | |
| 120 | + | for _, class := range contextMenuItemClasses() { | |
| 121 | + | btn.AddCssClass(class) | |
| 122 | + | } | |
| 99 | 123 | ||
| 100 | 124 | menuItem := item.item | |
| 101 | 125 | clickCb := func(_ gtk.Button) { | |
| @@ -112,6 +136,9 @@ func showMenu( | |||
| 112 | 136 | } | |
| 113 | 137 | ||
| 114 | 138 | popover = gtk.NewPopover() | |
| 139 | + | for _, class := range contextMenuPopoverClasses() { | |
| 140 | + | popover.AddCssClass(class) | |
| 141 | + | } | |
| 115 | 142 | popover.SetChild(&box.Widget) | |
| 116 | 143 | attachPopover(popover, popoverHost, parent) | |
| 117 | 144 | popover.SetHasArrow(false) | |
| @@ -11,6 +11,13 @@ type renderItem struct { | |||||
| 11 | separator bool | 11 | separator bool | ||
| 12 | } | 12 | } | ||
| 13 | 13 | ||||
| 14 | + | const ( | |||
| 15 | + | contextMenuBoxClass = "context-menu" | |||
| 16 | + | contextMenuPopoverClass = "context-menu-popover" | |||
| 17 | + | contextMenuItemClass = "context-menu-item" | |||
| 18 | + | contextMenuSeparatorClass = "context-menu-separator" | |||
| 19 | + | ) | |||
| 20 | + | ||||
| 14 | // Renderer shows GTK context menus on the provided anchor widget. | 21 | // Renderer shows GTK context menus on the provided anchor widget. | ||
| 15 | type Renderer struct { | 22 | type Renderer struct { | ||
| 16 | dispatch func(func()) | 23 | dispatch func(func()) | ||
| @@ -33,6 +40,18 @@ func BuildButtons(items []port.MenuItem) []renderItem { | |||||
| 33 | return renderItems | 40 | return renderItems | ||
| 34 | } | 41 | } | ||
| 35 | 42 | ||||
| 43 | + | func contextMenuItemClasses() []string { | |||
| 44 | + | return []string{"flat", contextMenuItemClass} | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | func contextMenuPopoverClasses() []string { | |||
| 48 | + | return []string{contextMenuPopoverClass} | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | func contextMenuSeparatorClasses() []string { | |||
| 52 | + | return []string{contextMenuSeparatorClass} | |||
| 53 | + | } | |||
| 54 | + | ||||
| 36 | func isSeparator(item port.MenuItem) bool { | 55 | func isSeparator(item port.MenuItem) bool { | ||
| 37 | return item.Action == "" && item.Label == "" | 56 | return item.Action == "" && item.Label == "" | ||
| 38 | } | 57 | } | ||
| @@ -79,7 +98,7 @@ func showMenu( | |||||
| 79 | } | 98 | } | ||
| 80 | 99 | ||||
| 81 | box := gtk.NewBox(gtk.OrientationVerticalValue, 0) | 100 | box := gtk.NewBox(gtk.OrientationVerticalValue, 0) | ||
| 82 | - | box.AddCssClass("context-menu") | 101 | + | box.AddCssClass(contextMenuBoxClass) |
| 83 | 102 | ||||
| 84 | selected := false | 103 | selected := false | ||
| 85 | var popover *gtk.Popover | 104 | var popover *gtk.Popover | ||
| @@ -89,13 +108,18 @@ func showMenu( | |||||
| 89 | for _, item := range items { | 108 | for _, item := range items { | ||
| 90 | if item.separator { | 109 | if item.separator { | ||
| 91 | sep := gtk.NewSeparator(gtk.OrientationHorizontalValue) | 110 | sep := gtk.NewSeparator(gtk.OrientationHorizontalValue) | ||
| 111 | + | for _, class := range contextMenuSeparatorClasses() { | |||
| 112 | + | sep.AddCssClass(class) | |||
| 113 | + | } | |||
| 92 | box.Append(&sep.Widget) | 114 | box.Append(&sep.Widget) | ||
| 93 | continue | 115 | continue | ||
| 94 | } | 116 | } | ||
| 95 | 117 | ||||
| 96 | btn := gtk.NewButton() | 118 | btn := gtk.NewButton() | ||
| 97 | btn.SetLabel(item.item.Label) | 119 | btn.SetLabel(item.item.Label) | ||
| 98 | - | btn.AddCssClass("flat") | 120 | + | for _, class := range contextMenuItemClasses() { |
| 121 | + | btn.AddCssClass(class) | |||
| 122 | + | } | |||
| 99 | 123 | ||||
| 100 | menuItem := item.item | 124 | menuItem := item.item | ||
| 101 | clickCb := func(_ gtk.Button) { | 125 | clickCb := func(_ gtk.Button) { | ||
| @@ -112,6 +136,9 @@ func showMenu( | |||||
| 112 | } | 136 | } | ||
| 113 | 137 | ||||
| 114 | popover = gtk.NewPopover() | 138 | popover = gtk.NewPopover() | ||
| 139 | + | for _, class := range contextMenuPopoverClasses() { | |||
| 140 | + | popover.AddCssClass(class) | |||
| 141 | + | } | |||
| 115 | popover.SetChild(&box.Widget) | 142 | popover.SetChild(&box.Widget) | ||
| 116 | attachPopover(popover, popoverHost, parent) | 143 | attachPopover(popover, popoverHost, parent) | ||
| 117 | popover.SetHasArrow(false) | 144 | popover.SetHasArrow(false) | ||
M internal/infrastructure/gtkmenu/renderer_test.go +12 -0
| @@ -62,3 +62,15 @@ func TestBuildButtons(t *testing.T) { | |||
| 62 | 62 | require.Equal(t, "Inspect Element", renderItems[3].item.Label) | |
| 63 | 63 | require.False(t, renderItems[3].separator) | |
| 64 | 64 | } | |
| 65 | + | ||
| 66 | + | func TestContextMenuItemClasses(t *testing.T) { | |
| 67 | + | require.Equal(t, []string{"flat", contextMenuItemClass}, contextMenuItemClasses()) | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func TestContextMenuSeparatorClasses(t *testing.T) { | |
| 71 | + | require.Equal(t, []string{contextMenuSeparatorClass}, contextMenuSeparatorClasses()) | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | func TestContextMenuPopoverClasses(t *testing.T) { | |
| 75 | + | require.Equal(t, []string{contextMenuPopoverClass}, contextMenuPopoverClasses()) | |
| 76 | + | } | |
| @@ -62,3 +62,15 @@ func TestBuildButtons(t *testing.T) { | |||||
| 62 | require.Equal(t, "Inspect Element", renderItems[3].item.Label) | 62 | require.Equal(t, "Inspect Element", renderItems[3].item.Label) | ||
| 63 | require.False(t, renderItems[3].separator) | 63 | require.False(t, renderItems[3].separator) | ||
| 64 | } | 64 | } | ||
| 65 | + | ||||
| 66 | + | func TestContextMenuItemClasses(t *testing.T) { | |||
| 67 | + | require.Equal(t, []string{"flat", contextMenuItemClass}, contextMenuItemClasses()) | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | func TestContextMenuSeparatorClasses(t *testing.T) { | |||
| 71 | + | require.Equal(t, []string{contextMenuSeparatorClass}, contextMenuSeparatorClasses()) | |||
| 72 | + | } | |||
| 73 | + | ||||
| 74 | + | func TestContextMenuPopoverClasses(t *testing.T) { | |||
| 75 | + | require.Equal(t, []string{contextMenuPopoverClass}, contextMenuPopoverClasses()) | |||
| 76 | + | } | |||
M internal/ui/theme/css.go +64 -0
| @@ -114,6 +114,10 @@ func GenerateCSSFull(p Palette, _ float64, fonts FontConfig, modeColors ModeColo | |||
| 114 | 114 | sb.WriteString(generateSessionManagerCSS(p)) | |
| 115 | 115 | sb.WriteString("\n") | |
| 116 | 116 | ||
| 117 | + | // Context menu styling | |
| 118 | + | sb.WriteString(generateContextMenuCSS(p)) | |
| 119 | + | sb.WriteString("\n") | |
| 120 | + | ||
| 117 | 121 | // Tab picker styling | |
| 118 | 122 | sb.WriteString(generateTabPickerCSS(p)) | |
| 119 | 123 | sb.WriteString("\n") | |
| @@ -902,6 +906,66 @@ row:selected .session-manager-row { | |||
| 902 | 906 | ` | |
| 903 | 907 | } | |
| 904 | 908 | ||
| 909 | + | // generateContextMenuCSS creates shared context menu styles. | |
| 910 | + | func generateContextMenuCSS(p Palette) string { | |
| 911 | + | _ = p | |
| 912 | + | return `/* ===== Context Menu Styling ===== */ | |
| 913 | + | ||
| 914 | + | popover.context-menu-popover { | |
| 915 | + | background-color: transparent; | |
| 916 | + | background-image: none; | |
| 917 | + | } | |
| 918 | + | ||
| 919 | + | popover.context-menu-popover > contents { | |
| 920 | + | background-color: var(--surface); | |
| 921 | + | background-image: none; | |
| 922 | + | border: 0.0625em solid alpha(var(--border), 0.85); | |
| 923 | + | border-radius: 0.25em; | |
| 924 | + | box-shadow: 0 0.75em 1.75em alpha(var(--bg), 0.28), 0 0.0625em 0 alpha(var(--border), 0.18) inset; | |
| 925 | + | padding: 0; | |
| 926 | + | } | |
| 927 | + | ||
| 928 | + | .context-menu { | |
| 929 | + | padding: 0.125em 0; | |
| 930 | + | min-width: 10.5em; | |
| 931 | + | } | |
| 932 | + | ||
| 933 | + | .context-menu button.context-menu-item { | |
| 934 | + | background-color: transparent; | |
| 935 | + | background-image: none; | |
| 936 | + | border: none; | |
| 937 | + | border-radius: 0; | |
| 938 | + | color: var(--text); | |
| 939 | + | padding: 0.25em 0.75em; | |
| 940 | + | margin: 0; | |
| 941 | + | min-height: 1.75em; | |
| 942 | + | transition: background-color 120ms ease-in-out, color 120ms ease-in-out; | |
| 943 | + | } | |
| 944 | + | ||
| 945 | + | .context-menu button.context-menu-item:hover, | |
| 946 | + | .context-menu button.context-menu-item:focus, | |
| 947 | + | .context-menu button.context-menu-item:active { | |
| 948 | + | background-color: alpha(var(--surface-variant), 0.9); | |
| 949 | + | color: var(--text); | |
| 950 | + | } | |
| 951 | + | ||
| 952 | + | .context-menu button.context-menu-item:active { | |
| 953 | + | background-color: alpha(var(--accent), 0.16); | |
| 954 | + | } | |
| 955 | + | ||
| 956 | + | .context-menu button.context-menu-item:disabled { | |
| 957 | + | color: var(--muted); | |
| 958 | + | opacity: 0.7; | |
| 959 | + | } | |
| 960 | + | ||
| 961 | + | .context-menu-separator { | |
| 962 | + | background-color: alpha(var(--border), 0.55); | |
| 963 | + | margin: 0.25em 0.5em; | |
| 964 | + | min-height: 0.0625em; | |
| 965 | + | } | |
| 966 | + | ` | |
| 967 | + | } | |
| 968 | + | ||
| 905 | 969 | // generateToasterCSS creates toast notification styles. | |
| 906 | 970 | // Uses em units for scalable UI. | |
| 907 | 971 | func generateToasterCSS(p Palette) string { | |
| @@ -114,6 +114,10 @@ func GenerateCSSFull(p Palette, _ float64, fonts FontConfig, modeColors ModeColo | |||||
| 114 | sb.WriteString(generateSessionManagerCSS(p)) | 114 | sb.WriteString(generateSessionManagerCSS(p)) | ||
| 115 | sb.WriteString("\n") | 115 | sb.WriteString("\n") | ||
| 116 | 116 | ||||
| 117 | + | // Context menu styling | |||
| 118 | + | sb.WriteString(generateContextMenuCSS(p)) | |||
| 119 | + | sb.WriteString("\n") | |||
| 120 | + | ||||
| 117 | // Tab picker styling | 121 | // Tab picker styling | ||
| 118 | sb.WriteString(generateTabPickerCSS(p)) | 122 | sb.WriteString(generateTabPickerCSS(p)) | ||
| 119 | sb.WriteString("\n") | 123 | sb.WriteString("\n") | ||
| @@ -902,6 +906,66 @@ row:selected .session-manager-row { | |||||
| 902 | ` | 906 | ` | ||
| 903 | } | 907 | } | ||
| 904 | 908 | ||||
| 909 | + | // generateContextMenuCSS creates shared context menu styles. | |||
| 910 | + | func generateContextMenuCSS(p Palette) string { | |||
| 911 | + | _ = p | |||
| 912 | + | return `/* ===== Context Menu Styling ===== */ | |||
| 913 | + | ||||
| 914 | + | popover.context-menu-popover { | |||
| 915 | + | background-color: transparent; | |||
| 916 | + | background-image: none; | |||
| 917 | + | } | |||
| 918 | + | ||||
| 919 | + | popover.context-menu-popover > contents { | |||
| 920 | + | background-color: var(--surface); | |||
| 921 | + | background-image: none; | |||
| 922 | + | border: 0.0625em solid alpha(var(--border), 0.85); | |||
| 923 | + | border-radius: 0.25em; | |||
| 924 | + | box-shadow: 0 0.75em 1.75em alpha(var(--bg), 0.28), 0 0.0625em 0 alpha(var(--border), 0.18) inset; | |||
| 925 | + | padding: 0; | |||
| 926 | + | } | |||
| 927 | + | ||||
| 928 | + | .context-menu { | |||
| 929 | + | padding: 0.125em 0; | |||
| 930 | + | min-width: 10.5em; | |||
| 931 | + | } | |||
| 932 | + | ||||
| 933 | + | .context-menu button.context-menu-item { | |||
| 934 | + | background-color: transparent; | |||
| 935 | + | background-image: none; | |||
| 936 | + | border: none; | |||
| 937 | + | border-radius: 0; | |||
| 938 | + | color: var(--text); | |||
| 939 | + | padding: 0.25em 0.75em; | |||
| 940 | + | margin: 0; | |||
| 941 | + | min-height: 1.75em; | |||
| 942 | + | transition: background-color 120ms ease-in-out, color 120ms ease-in-out; | |||
| 943 | + | } | |||
| 944 | + | ||||
| 945 | + | .context-menu button.context-menu-item:hover, | |||
| 946 | + | .context-menu button.context-menu-item:focus, | |||
| 947 | + | .context-menu button.context-menu-item:active { | |||
| 948 | + | background-color: alpha(var(--surface-variant), 0.9); | |||
| 949 | + | color: var(--text); | |||
| 950 | + | } | |||
| 951 | + | ||||
| 952 | + | .context-menu button.context-menu-item:active { | |||
| 953 | + | background-color: alpha(var(--accent), 0.16); | |||
| 954 | + | } | |||
| 955 | + | ||||
| 956 | + | .context-menu button.context-menu-item:disabled { | |||
| 957 | + | color: var(--muted); | |||
| 958 | + | opacity: 0.7; | |||
| 959 | + | } | |||
| 960 | + | ||||
| 961 | + | .context-menu-separator { | |||
| 962 | + | background-color: alpha(var(--border), 0.55); | |||
| 963 | + | margin: 0.25em 0.5em; | |||
| 964 | + | min-height: 0.0625em; | |||
| 965 | + | } | |||
| 966 | + | ` | |||
| 967 | + | } | |||
| 968 | + | ||||
| 905 | // generateToasterCSS creates toast notification styles. | 969 | // generateToasterCSS creates toast notification styles. | ||
| 906 | // Uses em units for scalable UI. | 970 | // Uses em units for scalable UI. | ||
| 907 | func generateToasterCSS(p Palette) string { | 971 | func generateToasterCSS(p Palette) string { | ||
M internal/ui/theme/css_test.go +47 -0
| @@ -108,6 +108,53 @@ func TestGenerateCSS_OmniboxSearchAreaMatchesHeaderSurface(t *testing.T) { | |||
| 108 | 108 | } | |
| 109 | 109 | } | |
| 110 | 110 | ||
| 111 | + | func TestGenerateCSS_ContextMenuUsesCompactSingleFrameStyling(t *testing.T) { | |
| 112 | + | css := GenerateCSS(DefaultDarkPalette()) | |
| 113 | + | ||
| 114 | + | assert.Contains(t, css, "popover.context-menu-popover > contents {") | |
| 115 | + | assert.Contains(t, css, ".context-menu {") | |
| 116 | + | assert.Contains(t, css, ".context-menu button.context-menu-item {") | |
| 117 | + | assert.Contains(t, css, ".context-menu button.context-menu-item:hover,") | |
| 118 | + | assert.Contains(t, css, ".context-menu button.context-menu-item:disabled {") | |
| 119 | + | assert.Contains(t, css, ".context-menu-separator {") | |
| 120 | + | ||
| 121 | + | popoverBlockRe := regexp.MustCompile(`(?s)popover\.context-menu-popover\s*>\s*contents\s*\{[^}]*\}`) | |
| 122 | + | popoverBlock := popoverBlockRe.FindString(css) | |
| 123 | + | if popoverBlock == "" { | |
| 124 | + | t.Fatal("expected context menu popover contents block in CSS") | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | assert.Contains(t, popoverBlock, "background-color: var(--surface);") | |
| 128 | + | assert.Contains(t, popoverBlock, "border: 0.0625em solid alpha(var(--border), 0.85);") | |
| 129 | + | assert.Contains(t, popoverBlock, "border-radius: 0.25em;") | |
| 130 | + | assert.Contains(t, popoverBlock, "box-shadow:") | |
| 131 | + | ||
| 132 | + | blockRe := regexp.MustCompile(`(?s)\.context-menu\s*\{[^}]*\}`) | |
| 133 | + | block := blockRe.FindString(css) | |
| 134 | + | if block == "" { | |
| 135 | + | t.Fatal("expected context menu block in CSS") | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | assert.NotContains(t, block, "background-color:") | |
| 139 | + | assert.Contains(t, block, "padding: 0.125em 0;") | |
| 140 | + | assert.Contains(t, block, "min-width: 10.5em;") | |
| 141 | + | assert.NotContains(t, block, "border:") | |
| 142 | + | assert.NotContains(t, block, "border-radius:") | |
| 143 | + | assert.NotContains(t, block, "box-shadow:") | |
| 144 | + | assert.NotContains(t, block, "#") | |
| 145 | + | assert.NotContains(t, block, "black") | |
| 146 | + | assert.NotContains(t, block, "white") | |
| 147 | + | ||
| 148 | + | itemBlockRe := regexp.MustCompile(`(?s)\.context-menu button\.context-menu-item\s*\{[^}]*\}`) | |
| 149 | + | itemBlock := itemBlockRe.FindString(css) | |
| 150 | + | if itemBlock == "" { | |
| 151 | + | t.Fatal("expected context menu item block in CSS") | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | assert.Contains(t, itemBlock, "padding: 0.25em 0.75em;") | |
| 155 | + | assert.Contains(t, itemBlock, "min-height: 1.75em;") | |
| 156 | + | } | |
| 157 | + | ||
| 111 | 158 | func TestGenerateCSSWithScale_DoesNotEmitRootTextScaling(t *testing.T) { | |
| 112 | 159 | css := GenerateCSSWithScale(DefaultDarkPalette(), 1.3) | |
| 113 | 160 | ||
| @@ -108,6 +108,53 @@ func TestGenerateCSS_OmniboxSearchAreaMatchesHeaderSurface(t *testing.T) { | |||||
| 108 | } | 108 | } | ||
| 109 | } | 109 | } | ||
| 110 | 110 | ||||
| 111 | + | func TestGenerateCSS_ContextMenuUsesCompactSingleFrameStyling(t *testing.T) { | |||
| 112 | + | css := GenerateCSS(DefaultDarkPalette()) | |||
| 113 | + | ||||
| 114 | + | assert.Contains(t, css, "popover.context-menu-popover > contents {") | |||
| 115 | + | assert.Contains(t, css, ".context-menu {") | |||
| 116 | + | assert.Contains(t, css, ".context-menu button.context-menu-item {") | |||
| 117 | + | assert.Contains(t, css, ".context-menu button.context-menu-item:hover,") | |||
| 118 | + | assert.Contains(t, css, ".context-menu button.context-menu-item:disabled {") | |||
| 119 | + | assert.Contains(t, css, ".context-menu-separator {") | |||
| 120 | + | ||||
| 121 | + | popoverBlockRe := regexp.MustCompile(`(?s)popover\.context-menu-popover\s*>\s*contents\s*\{[^}]*\}`) | |||
| 122 | + | popoverBlock := popoverBlockRe.FindString(css) | |||
| 123 | + | if popoverBlock == "" { | |||
| 124 | + | t.Fatal("expected context menu popover contents block in CSS") | |||
| 125 | + | } | |||
| 126 | + | ||||
| 127 | + | assert.Contains(t, popoverBlock, "background-color: var(--surface);") | |||
| 128 | + | assert.Contains(t, popoverBlock, "border: 0.0625em solid alpha(var(--border), 0.85);") | |||
| 129 | + | assert.Contains(t, popoverBlock, "border-radius: 0.25em;") | |||
| 130 | + | assert.Contains(t, popoverBlock, "box-shadow:") | |||
| 131 | + | ||||
| 132 | + | blockRe := regexp.MustCompile(`(?s)\.context-menu\s*\{[^}]*\}`) | |||
| 133 | + | block := blockRe.FindString(css) | |||
| 134 | + | if block == "" { | |||
| 135 | + | t.Fatal("expected context menu block in CSS") | |||
| 136 | + | } | |||
| 137 | + | ||||
| 138 | + | assert.NotContains(t, block, "background-color:") | |||
| 139 | + | assert.Contains(t, block, "padding: 0.125em 0;") | |||
| 140 | + | assert.Contains(t, block, "min-width: 10.5em;") | |||
| 141 | + | assert.NotContains(t, block, "border:") | |||
| 142 | + | assert.NotContains(t, block, "border-radius:") | |||
| 143 | + | assert.NotContains(t, block, "box-shadow:") | |||
| 144 | + | assert.NotContains(t, block, "#") | |||
| 145 | + | assert.NotContains(t, block, "black") | |||
| 146 | + | assert.NotContains(t, block, "white") | |||
| 147 | + | ||||
| 148 | + | itemBlockRe := regexp.MustCompile(`(?s)\.context-menu button\.context-menu-item\s*\{[^}]*\}`) | |||
| 149 | + | itemBlock := itemBlockRe.FindString(css) | |||
| 150 | + | if itemBlock == "" { | |||
| 151 | + | t.Fatal("expected context menu item block in CSS") | |||
| 152 | + | } | |||
| 153 | + | ||||
| 154 | + | assert.Contains(t, itemBlock, "padding: 0.25em 0.75em;") | |||
| 155 | + | assert.Contains(t, itemBlock, "min-height: 1.75em;") | |||
| 156 | + | } | |||
| 157 | + | ||||
| 111 | func TestGenerateCSSWithScale_DoesNotEmitRootTextScaling(t *testing.T) { | 158 | func TestGenerateCSSWithScale_DoesNotEmitRootTextScaling(t *testing.T) { | ||
| 112 | css := GenerateCSSWithScale(DefaultDarkPalette(), 1.3) | 159 | css := GenerateCSSWithScale(DefaultDarkPalette(), 1.3) | ||
| 113 | 160 | ||||