fix(permission): work around purego bool bug and fix linting
- Use GObject property accessors instead of C function wrappers in determinePermissionTypes to avoid purego bool return value bug - Refactor permission_popup.go to fix funlen and mnd linter issues: - Extract setupContainers() and setupLabels() helpers - Add createPermissionButton() and wireButton() helpers - Add buttonSpacing constant to replace magic number
- commit
- 2db8eeb50b581584429de12642231982760f42f6
- author
- brice · · 2026-01-30 06:38 UTC
- committer
- brice · · 2026-02-07 11:58 UTC
- parent
- 9bb5879
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +71 -44
M internal/infrastructure/webkit/webview.go +14 -3
| @@ -1004,17 +1004,28 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||
| 1004 | 1004 | ||
| 1005 | 1005 | // determinePermissionTypes extracts permission types from a WebKit permission request. | |
| 1006 | 1006 | // This uses type checking to identify UserMediaPermissionRequest and its specific types. | |
| 1007 | + | // We use GObject property accessors as the primary method because the C function wrappers | |
| 1008 | + | // can hit a purego bug where bool return values are misread. | |
| 1007 | 1009 | func (wv *WebView) determinePermissionTypes(requestPtr uintptr) []string { | |
| 1008 | - | _ = wv // suppress unused receiver warning, method may need receiver in future | |
| 1009 | 1010 | var types []string | |
| 1010 | 1011 | ||
| 1011 | 1012 | // Try to cast to UserMediaPermissionRequest | |
| 1012 | 1013 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | |
| 1013 | 1014 | if userMediaReq != nil { | |
| 1014 | - | if webkit.UserMediaPermissionIsForAudioDevice(userMediaReq) { | |
| 1015 | + | // Use GObject property accessors — more reliable than the C function wrappers | |
| 1016 | + | // which can hit the purego bool return value bug. | |
| 1017 | + | isAudio := userMediaReq.GetPropertyIsForAudioDevice() | |
| 1018 | + | isVideo := userMediaReq.GetPropertyIsForVideoDevice() | |
| 1019 | + | ||
| 1020 | + | wv.logger.Debug(). | |
| 1021 | + | Bool("is_audio", isAudio). | |
| 1022 | + | Bool("is_video", isVideo). | |
| 1023 | + | Msg("permission request type detection") | |
| 1024 | + | ||
| 1025 | + | if isAudio { | |
| 1015 | 1026 | types = append(types, "microphone") | |
| 1016 | 1027 | } | |
| 1017 | - | if webkit.UserMediaPermissionIsForVideoDevice(userMediaReq) { | |
| 1028 | + | if isVideo { | |
| 1018 | 1029 | // Check if this is display capture or camera | |
| 1019 | 1030 | if webkit.UserMediaPermissionIsForDisplayDevice(userMediaReq) { | |
| 1020 | 1031 | types = append(types, "display") | |
| @@ -1004,17 +1004,28 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||||
| 1004 | 1004 | ||||
| 1005 | // determinePermissionTypes extracts permission types from a WebKit permission request. | 1005 | // determinePermissionTypes extracts permission types from a WebKit permission request. | ||
| 1006 | // This uses type checking to identify UserMediaPermissionRequest and its specific types. | 1006 | // This uses type checking to identify UserMediaPermissionRequest and its specific types. | ||
| 1007 | + | // We use GObject property accessors as the primary method because the C function wrappers | |||
| 1008 | + | // can hit a purego bug where bool return values are misread. | |||
| 1007 | func (wv *WebView) determinePermissionTypes(requestPtr uintptr) []string { | 1009 | func (wv *WebView) determinePermissionTypes(requestPtr uintptr) []string { | ||
| 1008 | - | _ = wv // suppress unused receiver warning, method may need receiver in future | |||
| 1009 | var types []string | 1010 | var types []string | ||
| 1010 | 1011 | ||||
| 1011 | // Try to cast to UserMediaPermissionRequest | 1012 | // Try to cast to UserMediaPermissionRequest | ||
| 1012 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | 1013 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | ||
| 1013 | if userMediaReq != nil { | 1014 | if userMediaReq != nil { | ||
| 1014 | - | if webkit.UserMediaPermissionIsForAudioDevice(userMediaReq) { | 1015 | + | // Use GObject property accessors — more reliable than the C function wrappers |
| 1016 | + | // which can hit the purego bool return value bug. | |||
| 1017 | + | isAudio := userMediaReq.GetPropertyIsForAudioDevice() | |||
| 1018 | + | isVideo := userMediaReq.GetPropertyIsForVideoDevice() | |||
| 1019 | + | ||||
| 1020 | + | wv.logger.Debug(). | |||
| 1021 | + | Bool("is_audio", isAudio). | |||
| 1022 | + | Bool("is_video", isVideo). | |||
| 1023 | + | Msg("permission request type detection") | |||
| 1024 | + | ||||
| 1025 | + | if isAudio { | |||
| 1015 | types = append(types, "microphone") | 1026 | types = append(types, "microphone") | ||
| 1016 | } | 1027 | } | ||
| 1017 | - | if webkit.UserMediaPermissionIsForVideoDevice(userMediaReq) { | 1028 | + | if isVideo { |
| 1018 | // Check if this is display capture or camera | 1029 | // Check if this is display capture or camera | ||
| 1019 | if webkit.UserMediaPermissionIsForDisplayDevice(userMediaReq) { | 1030 | if webkit.UserMediaPermissionIsForDisplayDevice(userMediaReq) { | ||
| 1020 | types = append(types, "display") | 1031 | types = append(types, "display") | ||
M internal/ui/component/permission_popup.go +57 -41
| @@ -10,6 +10,9 @@ import ( | |||
| 10 | 10 | "github.com/jwijenbergh/puregotk/v4/gtk" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | + | // buttonSpacing is the spacing between buttons in the permission popup. | |
| 14 | + | const buttonSpacing = 6 | |
| 15 | + | ||
| 13 | 16 | // PermissionPopup is a custom overlay component for permission prompts. | |
| 14 | 17 | // It replaces the Adwaita AlertDialog to sidestep the purego ConnectResponse bug | |
| 15 | 18 | // and match the app's custom UI style. | |
| @@ -135,8 +138,8 @@ func (pp *PermissionPopup) dismiss(allowed, persistent bool) { | |||
| 135 | 138 | } | |
| 136 | 139 | } | |
| 137 | 140 | ||
| 138 | - | func (pp *PermissionPopup) createWidgets() error { | |
| 139 | - | // Outer box - positioned in overlay, hidden by default | |
| 141 | + | // setupContainers creates and configures the outer and main boxes. | |
| 142 | + | func (pp *PermissionPopup) setupContainers() error { | |
| 140 | 143 | pp.outerBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | |
| 141 | 144 | if pp.outerBox == nil { | |
| 142 | 145 | return errNilWidget("permissionPopupOuterBox") | |
| @@ -146,14 +149,16 @@ func (pp *PermissionPopup) createWidgets() error { | |||
| 146 | 149 | pp.outerBox.SetValign(gtk.AlignStartValue) | |
| 147 | 150 | pp.outerBox.SetVisible(false) | |
| 148 | 151 | ||
| 149 | - | // Main box - styled container | |
| 150 | 152 | pp.mainBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | |
| 151 | 153 | if pp.mainBox == nil { | |
| 152 | 154 | return errNilWidget("permissionPopupMainBox") | |
| 153 | 155 | } | |
| 154 | 156 | pp.mainBox.AddCssClass("permission-popup-container") | |
| 157 | + | return nil | |
| 158 | + | } | |
| 155 | 159 | ||
| 156 | - | // Heading label | |
| 160 | + | // setupLabels creates and configures the heading and body labels. | |
| 161 | + | func (pp *PermissionPopup) setupLabels() error { | |
| 157 | 162 | emptyText := "" | |
| 158 | 163 | pp.headingLabel = gtk.NewLabel(&emptyText) | |
| 159 | 164 | if pp.headingLabel == nil { | |
| @@ -162,7 +167,6 @@ func (pp *PermissionPopup) createWidgets() error { | |||
| 162 | 167 | pp.headingLabel.AddCssClass("permission-popup-heading") | |
| 163 | 168 | pp.headingLabel.SetHalign(gtk.AlignStartValue) | |
| 164 | 169 | ||
| 165 | - | // Body label | |
| 166 | 170 | pp.bodyLabel = gtk.NewLabel(&emptyText) | |
| 167 | 171 | if pp.bodyLabel == nil { | |
| 168 | 172 | return errNilWidget("permissionPopupBodyLabel") | |
| @@ -170,9 +174,20 @@ func (pp *PermissionPopup) createWidgets() error { | |||
| 170 | 174 | pp.bodyLabel.AddCssClass("permission-popup-body") | |
| 171 | 175 | pp.bodyLabel.SetHalign(gtk.AlignStartValue) | |
| 172 | 176 | pp.bodyLabel.SetWrap(true) | |
| 177 | + | return nil | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | func (pp *PermissionPopup) createWidgets() error { | |
| 181 | + | if err := pp.setupContainers(); err != nil { | |
| 182 | + | return err | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | if err := pp.setupLabels(); err != nil { | |
| 186 | + | return err | |
| 187 | + | } | |
| 173 | 188 | ||
| 174 | 189 | // Button row | |
| 175 | - | btnRow := gtk.NewBox(gtk.OrientationHorizontalValue, 6) | |
| 190 | + | btnRow := gtk.NewBox(gtk.OrientationHorizontalValue, buttonSpacing) | |
| 176 | 191 | if btnRow == nil { | |
| 177 | 192 | return errNilWidget("permissionPopupBtnRow") | |
| 178 | 193 | } | |
| @@ -180,50 +195,32 @@ func (pp *PermissionPopup) createWidgets() error { | |||
| 180 | 195 | btnRow.SetHalign(gtk.AlignEndValue) | |
| 181 | 196 | ||
| 182 | 197 | // Create buttons: Always Deny | Deny | Allow | Always Allow | |
| 183 | - | pp.btnAlwaysDeny = gtk.NewButtonWithLabel("Always Deny") | |
| 184 | - | if pp.btnAlwaysDeny == nil { | |
| 185 | - | return errNilWidget("permissionPopupBtnAlwaysDeny") | |
| 198 | + | var err error | |
| 199 | + | pp.btnAlwaysDeny, err = pp.createPermissionButton("Always Deny", []string{"permission-popup-btn", "permission-popup-btn-destructive"}) | |
| 200 | + | if err != nil { | |
| 201 | + | return err | |
| 186 | 202 | } | |
| 187 | - | pp.btnAlwaysDeny.AddCssClass("permission-popup-btn") | |
| 188 | - | pp.btnAlwaysDeny.AddCssClass("permission-popup-btn-destructive") | |
| 189 | 203 | ||
| 190 | - | pp.btnDeny = gtk.NewButtonWithLabel("Deny") | |
| 191 | - | if pp.btnDeny == nil { | |
| 192 | - | return errNilWidget("permissionPopupBtnDeny") | |
| 204 | + | pp.btnDeny, err = pp.createPermissionButton("Deny", []string{"permission-popup-btn", "permission-popup-btn-deny"}) | |
| 205 | + | if err != nil { | |
| 206 | + | return err | |
| 193 | 207 | } | |
| 194 | - | pp.btnDeny.AddCssClass("permission-popup-btn") | |
| 195 | - | pp.btnDeny.AddCssClass("permission-popup-btn-deny") | |
| 196 | 208 | ||
| 197 | - | pp.btnAllow = gtk.NewButtonWithLabel("Allow") | |
| 198 | - | if pp.btnAllow == nil { | |
| 199 | - | return errNilWidget("permissionPopupBtnAllow") | |
| 209 | + | pp.btnAllow, err = pp.createPermissionButton("Allow", []string{"permission-popup-btn", "permission-popup-btn-allow"}) | |
| 210 | + | if err != nil { | |
| 211 | + | return err | |
| 200 | 212 | } | |
| 201 | - | pp.btnAllow.AddCssClass("permission-popup-btn") | |
| 202 | - | pp.btnAllow.AddCssClass("permission-popup-btn-allow") | |
| 203 | 213 | ||
| 204 | - | pp.btnAlwaysAllow = gtk.NewButtonWithLabel("Always Allow") | |
| 205 | - | if pp.btnAlwaysAllow == nil { | |
| 206 | - | return errNilWidget("permissionPopupBtnAlwaysAllow") | |
| 214 | + | pp.btnAlwaysAllow, err = pp.createPermissionButton("Always Allow", []string{"permission-popup-btn", "permission-popup-btn-allow"}) | |
| 215 | + | if err != nil { | |
| 216 | + | return err | |
| 207 | 217 | } | |
| 208 | - | pp.btnAlwaysAllow.AddCssClass("permission-popup-btn") | |
| 209 | - | pp.btnAlwaysAllow.AddCssClass("permission-popup-btn-allow") | |
| 210 | 218 | ||
| 211 | 219 | // Wire button clicks | |
| 212 | - | alwaysDenyCb := func(_ gtk.Button) { pp.dismiss(false, true) } | |
| 213 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, alwaysDenyCb) | |
| 214 | - | pp.btnAlwaysDeny.ConnectClicked(&alwaysDenyCb) | |
| 215 | - | ||
| 216 | - | denyCb := func(_ gtk.Button) { pp.dismiss(false, false) } | |
| 217 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, denyCb) | |
| 218 | - | pp.btnDeny.ConnectClicked(&denyCb) | |
| 219 | - | ||
| 220 | - | allowCb := func(_ gtk.Button) { pp.dismiss(true, false) } | |
| 221 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, allowCb) | |
| 222 | - | pp.btnAllow.ConnectClicked(&allowCb) | |
| 223 | - | ||
| 224 | - | alwaysAllowCb := func(_ gtk.Button) { pp.dismiss(true, true) } | |
| 225 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, alwaysAllowCb) | |
| 226 | - | pp.btnAlwaysAllow.ConnectClicked(&alwaysAllowCb) | |
| 220 | + | pp.wireButton(pp.btnAlwaysDeny, false, true) | |
| 221 | + | pp.wireButton(pp.btnDeny, false, false) | |
| 222 | + | pp.wireButton(pp.btnAllow, true, false) | |
| 223 | + | pp.wireButton(pp.btnAlwaysAllow, true, true) | |
| 227 | 224 | ||
| 228 | 225 | // Assemble button row | |
| 229 | 226 | btnRow.Append(&pp.btnAlwaysDeny.Widget) | |
| @@ -242,6 +239,25 @@ func (pp *PermissionPopup) createWidgets() error { | |||
| 242 | 239 | return nil | |
| 243 | 240 | } | |
| 244 | 241 | ||
| 242 | + | // createPermissionButton creates a permission button with the given label and CSS classes. | |
| 243 | + | func (pp *PermissionPopup) createPermissionButton(label string, cssClasses []string) (*gtk.Button, error) { | |
| 244 | + | btn := gtk.NewButtonWithLabel(label) | |
| 245 | + | if btn == nil { | |
| 246 | + | return nil, errNilWidget("permissionPopupBtn" + label) | |
| 247 | + | } | |
| 248 | + | for _, class := range cssClasses { | |
| 249 | + | btn.AddCssClass(class) | |
| 250 | + | } | |
| 251 | + | return btn, nil | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | // wireButton connects a button click to the dismiss callback. | |
| 255 | + | func (pp *PermissionPopup) wireButton(btn *gtk.Button, allowed, persistent bool) { | |
| 256 | + | cb := func(_ gtk.Button) { pp.dismiss(allowed, persistent) } | |
| 257 | + | pp.retainedCallbacks = append(pp.retainedCallbacks, cb) | |
| 258 | + | btn.ConnectClicked(&cb) | |
| 259 | + | } | |
| 260 | + | ||
| 245 | 261 | func (pp *PermissionPopup) attachKeyController() { | |
| 246 | 262 | if pp.outerBox == nil { | |
| 247 | 263 | return | |
| @@ -10,6 +10,9 @@ import ( | |||||
| 10 | "github.com/jwijenbergh/puregotk/v4/gtk" | 10 | "github.com/jwijenbergh/puregotk/v4/gtk" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | + | // buttonSpacing is the spacing between buttons in the permission popup. | |||
| 14 | + | const buttonSpacing = 6 | |||
| 15 | + | ||||
| 13 | // PermissionPopup is a custom overlay component for permission prompts. | 16 | // PermissionPopup is a custom overlay component for permission prompts. | ||
| 14 | // It replaces the Adwaita AlertDialog to sidestep the purego ConnectResponse bug | 17 | // It replaces the Adwaita AlertDialog to sidestep the purego ConnectResponse bug | ||
| 15 | // and match the app's custom UI style. | 18 | // and match the app's custom UI style. | ||
| @@ -135,8 +138,8 @@ func (pp *PermissionPopup) dismiss(allowed, persistent bool) { | |||||
| 135 | } | 138 | } | ||
| 136 | } | 139 | } | ||
| 137 | 140 | ||||
| 138 | - | func (pp *PermissionPopup) createWidgets() error { | 141 | + | // setupContainers creates and configures the outer and main boxes. |
| 139 | - | // Outer box - positioned in overlay, hidden by default | 142 | + | func (pp *PermissionPopup) setupContainers() error { |
| 140 | pp.outerBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | 143 | pp.outerBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | ||
| 141 | if pp.outerBox == nil { | 144 | if pp.outerBox == nil { | ||
| 142 | return errNilWidget("permissionPopupOuterBox") | 145 | return errNilWidget("permissionPopupOuterBox") | ||
| @@ -146,14 +149,16 @@ func (pp *PermissionPopup) createWidgets() error { | |||||
| 146 | pp.outerBox.SetValign(gtk.AlignStartValue) | 149 | pp.outerBox.SetValign(gtk.AlignStartValue) | ||
| 147 | pp.outerBox.SetVisible(false) | 150 | pp.outerBox.SetVisible(false) | ||
| 148 | 151 | ||||
| 149 | - | // Main box - styled container | |||
| 150 | pp.mainBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | 152 | pp.mainBox = gtk.NewBox(gtk.OrientationVerticalValue, 0) | ||
| 151 | if pp.mainBox == nil { | 153 | if pp.mainBox == nil { | ||
| 152 | return errNilWidget("permissionPopupMainBox") | 154 | return errNilWidget("permissionPopupMainBox") | ||
| 153 | } | 155 | } | ||
| 154 | pp.mainBox.AddCssClass("permission-popup-container") | 156 | pp.mainBox.AddCssClass("permission-popup-container") | ||
| 157 | + | return nil | |||
| 158 | + | } | |||
| 155 | 159 | ||||
| 156 | - | // Heading label | 160 | + | // setupLabels creates and configures the heading and body labels. |
| 161 | + | func (pp *PermissionPopup) setupLabels() error { | |||
| 157 | emptyText := "" | 162 | emptyText := "" | ||
| 158 | pp.headingLabel = gtk.NewLabel(&emptyText) | 163 | pp.headingLabel = gtk.NewLabel(&emptyText) | ||
| 159 | if pp.headingLabel == nil { | 164 | if pp.headingLabel == nil { | ||
| @@ -162,7 +167,6 @@ func (pp *PermissionPopup) createWidgets() error { | |||||
| 162 | pp.headingLabel.AddCssClass("permission-popup-heading") | 167 | pp.headingLabel.AddCssClass("permission-popup-heading") | ||
| 163 | pp.headingLabel.SetHalign(gtk.AlignStartValue) | 168 | pp.headingLabel.SetHalign(gtk.AlignStartValue) | ||
| 164 | 169 | ||||
| 165 | - | // Body label | |||
| 166 | pp.bodyLabel = gtk.NewLabel(&emptyText) | 170 | pp.bodyLabel = gtk.NewLabel(&emptyText) | ||
| 167 | if pp.bodyLabel == nil { | 171 | if pp.bodyLabel == nil { | ||
| 168 | return errNilWidget("permissionPopupBodyLabel") | 172 | return errNilWidget("permissionPopupBodyLabel") | ||
| @@ -170,9 +174,20 @@ func (pp *PermissionPopup) createWidgets() error { | |||||
| 170 | pp.bodyLabel.AddCssClass("permission-popup-body") | 174 | pp.bodyLabel.AddCssClass("permission-popup-body") | ||
| 171 | pp.bodyLabel.SetHalign(gtk.AlignStartValue) | 175 | pp.bodyLabel.SetHalign(gtk.AlignStartValue) | ||
| 172 | pp.bodyLabel.SetWrap(true) | 176 | pp.bodyLabel.SetWrap(true) | ||
| 177 | + | return nil | |||
| 178 | + | } | |||
| 179 | + | ||||
| 180 | + | func (pp *PermissionPopup) createWidgets() error { | |||
| 181 | + | if err := pp.setupContainers(); err != nil { | |||
| 182 | + | return err | |||
| 183 | + | } | |||
| 184 | + | ||||
| 185 | + | if err := pp.setupLabels(); err != nil { | |||
| 186 | + | return err | |||
| 187 | + | } | |||
| 173 | 188 | ||||
| 174 | // Button row | 189 | // Button row | ||
| 175 | - | btnRow := gtk.NewBox(gtk.OrientationHorizontalValue, 6) | 190 | + | btnRow := gtk.NewBox(gtk.OrientationHorizontalValue, buttonSpacing) |
| 176 | if btnRow == nil { | 191 | if btnRow == nil { | ||
| 177 | return errNilWidget("permissionPopupBtnRow") | 192 | return errNilWidget("permissionPopupBtnRow") | ||
| 178 | } | 193 | } | ||
| @@ -180,50 +195,32 @@ func (pp *PermissionPopup) createWidgets() error { | |||||
| 180 | btnRow.SetHalign(gtk.AlignEndValue) | 195 | btnRow.SetHalign(gtk.AlignEndValue) | ||
| 181 | 196 | ||||
| 182 | // Create buttons: Always Deny | Deny | Allow | Always Allow | 197 | // Create buttons: Always Deny | Deny | Allow | Always Allow | ||
| 183 | - | pp.btnAlwaysDeny = gtk.NewButtonWithLabel("Always Deny") | 198 | + | var err error |
| 184 | - | if pp.btnAlwaysDeny == nil { | 199 | + | pp.btnAlwaysDeny, err = pp.createPermissionButton("Always Deny", []string{"permission-popup-btn", "permission-popup-btn-destructive"}) |
| 185 | - | return errNilWidget("permissionPopupBtnAlwaysDeny") | 200 | + | if err != nil { |
| 201 | + | return err | |||
| 186 | } | 202 | } | ||
| 187 | - | pp.btnAlwaysDeny.AddCssClass("permission-popup-btn") | |||
| 188 | - | pp.btnAlwaysDeny.AddCssClass("permission-popup-btn-destructive") | |||
| 189 | 203 | ||||
| 190 | - | pp.btnDeny = gtk.NewButtonWithLabel("Deny") | 204 | + | pp.btnDeny, err = pp.createPermissionButton("Deny", []string{"permission-popup-btn", "permission-popup-btn-deny"}) |
| 191 | - | if pp.btnDeny == nil { | 205 | + | if err != nil { |
| 192 | - | return errNilWidget("permissionPopupBtnDeny") | 206 | + | return err |
| 193 | } | 207 | } | ||
| 194 | - | pp.btnDeny.AddCssClass("permission-popup-btn") | |||
| 195 | - | pp.btnDeny.AddCssClass("permission-popup-btn-deny") | |||
| 196 | 208 | ||||
| 197 | - | pp.btnAllow = gtk.NewButtonWithLabel("Allow") | 209 | + | pp.btnAllow, err = pp.createPermissionButton("Allow", []string{"permission-popup-btn", "permission-popup-btn-allow"}) |
| 198 | - | if pp.btnAllow == nil { | 210 | + | if err != nil { |
| 199 | - | return errNilWidget("permissionPopupBtnAllow") | 211 | + | return err |
| 200 | } | 212 | } | ||
| 201 | - | pp.btnAllow.AddCssClass("permission-popup-btn") | |||
| 202 | - | pp.btnAllow.AddCssClass("permission-popup-btn-allow") | |||
| 203 | 213 | ||||
| 204 | - | pp.btnAlwaysAllow = gtk.NewButtonWithLabel("Always Allow") | 214 | + | pp.btnAlwaysAllow, err = pp.createPermissionButton("Always Allow", []string{"permission-popup-btn", "permission-popup-btn-allow"}) |
| 205 | - | if pp.btnAlwaysAllow == nil { | 215 | + | if err != nil { |
| 206 | - | return errNilWidget("permissionPopupBtnAlwaysAllow") | 216 | + | return err |
| 207 | } | 217 | } | ||
| 208 | - | pp.btnAlwaysAllow.AddCssClass("permission-popup-btn") | |||
| 209 | - | pp.btnAlwaysAllow.AddCssClass("permission-popup-btn-allow") | |||
| 210 | 218 | ||||
| 211 | // Wire button clicks | 219 | // Wire button clicks | ||
| 212 | - | alwaysDenyCb := func(_ gtk.Button) { pp.dismiss(false, true) } | 220 | + | pp.wireButton(pp.btnAlwaysDeny, false, true) |
| 213 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, alwaysDenyCb) | 221 | + | pp.wireButton(pp.btnDeny, false, false) |
| 214 | - | pp.btnAlwaysDeny.ConnectClicked(&alwaysDenyCb) | 222 | + | pp.wireButton(pp.btnAllow, true, false) |
| 215 | - | 223 | + | pp.wireButton(pp.btnAlwaysAllow, true, true) | |
| 216 | - | denyCb := func(_ gtk.Button) { pp.dismiss(false, false) } | |||
| 217 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, denyCb) | |||
| 218 | - | pp.btnDeny.ConnectClicked(&denyCb) | |||
| 219 | - | ||||
| 220 | - | allowCb := func(_ gtk.Button) { pp.dismiss(true, false) } | |||
| 221 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, allowCb) | |||
| 222 | - | pp.btnAllow.ConnectClicked(&allowCb) | |||
| 223 | - | ||||
| 224 | - | alwaysAllowCb := func(_ gtk.Button) { pp.dismiss(true, true) } | |||
| 225 | - | pp.retainedCallbacks = append(pp.retainedCallbacks, alwaysAllowCb) | |||
| 226 | - | pp.btnAlwaysAllow.ConnectClicked(&alwaysAllowCb) | |||
| 227 | 224 | ||||
| 228 | // Assemble button row | 225 | // Assemble button row | ||
| 229 | btnRow.Append(&pp.btnAlwaysDeny.Widget) | 226 | btnRow.Append(&pp.btnAlwaysDeny.Widget) | ||
| @@ -242,6 +239,25 @@ func (pp *PermissionPopup) createWidgets() error { | |||||
| 242 | return nil | 239 | return nil | ||
| 243 | } | 240 | } | ||
| 244 | 241 | ||||
| 242 | + | // createPermissionButton creates a permission button with the given label and CSS classes. | |||
| 243 | + | func (pp *PermissionPopup) createPermissionButton(label string, cssClasses []string) (*gtk.Button, error) { | |||
| 244 | + | btn := gtk.NewButtonWithLabel(label) | |||
| 245 | + | if btn == nil { | |||
| 246 | + | return nil, errNilWidget("permissionPopupBtn" + label) | |||
| 247 | + | } | |||
| 248 | + | for _, class := range cssClasses { | |||
| 249 | + | btn.AddCssClass(class) | |||
| 250 | + | } | |||
| 251 | + | return btn, nil | |||
| 252 | + | } | |||
| 253 | + | ||||
| 254 | + | // wireButton connects a button click to the dismiss callback. | |||
| 255 | + | func (pp *PermissionPopup) wireButton(btn *gtk.Button, allowed, persistent bool) { | |||
| 256 | + | cb := func(_ gtk.Button) { pp.dismiss(allowed, persistent) } | |||
| 257 | + | pp.retainedCallbacks = append(pp.retainedCallbacks, cb) | |||
| 258 | + | btn.ConnectClicked(&cb) | |||
| 259 | + | } | |||
| 260 | + | ||||
| 245 | func (pp *PermissionPopup) attachKeyController() { | 261 | func (pp *PermissionPopup) attachKeyController() { | ||
| 246 | if pp.outerBox == nil { | 262 | if pp.outerBox == nil { | ||
| 247 | return | 263 | return | ||