fix: loading skeleton redesign, review follow-ups, and changelog update
Loading skeleton: - Reduce logo from 512px to 256px with version label and 32px spinner - Extract sizes to constants, tests use mock.Anything for dimensions - Set version via SetSkeletonVersion() from main.go during GUI startup Review follow-ups: - Return error when visit delta exceeds fallback cap (not silent drop) - Differentiate os.IsNotExist from other OpenFile errors in markAbruptExits - Add nolint:staticcheck to url.Error.Temporary() for consistency - Make reusePolicyAggressive reuse regardless of navigation activity - Inline crashReloadTarget no-op wrapper - Fix "Stay on this page" button to actually stay on the crash page Changelog: document all unreleased skeleton, pool, retry, session, crash page, and reuse policy changes.
- commit
- 88eec0a2442d4dbf452b80cc3a2802fef2b49c56
- author
- brice · · 2026-02-07 11:10 UTC
- parent
- 6a42479
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
13 files · +183 -90
M CHANGELOG.md +7 -0
| @@ -18,6 +18,8 @@ All notable changes to this project will be documented in this file. | |||
| 18 | 18 | - **Autocomplete architecture cleanup**: Centralized completion flow and ghost positioning helpers for more consistent behavior across async paths. | |
| 19 | 19 | - **Omnibox keyboard/navigation UX**: Added `Alt+h/j/k/l` pane navigation while omnibox is open and improved chrome height scaling/debouncing on GTK main thread. | |
| 20 | 20 | - **Config normalization hardening**: Normalization now coerces invalid profile/appearance values to safe defaults. | |
| 21 | + | - **Loading skeleton redesign**: Reduced logo from 512px to 256px, added version label below a discrete 32px spinner. Sizes extracted to constants so tests use `mock.Anything` and don't break on dimension changes. | |
| 22 | + | - **Aggressive WebView reuse policy**: `reusePolicyAggressive` now actually differs from `reusePolicySafe` by allowing reuse regardless of navigation activity, relying on `ResetForPoolReuse` to clear state. | |
| 21 | 23 | ||
| 22 | 24 | ### Fixed | |
| 23 | 25 | ||
| @@ -33,6 +35,11 @@ All notable changes to this project will be documented in this file. | |||
| 33 | 35 | - **Navigation safety guards**: Added `CanGoBack` protection in navigation policy handling and corrected back-forward-list signal disconnection target. | |
| 34 | 36 | - **Crash handling and retry edge-cases**: Hardened crash-page routing safety, updater retry semantics/documentation, and related infra error paths. | |
| 35 | 37 | - **Filtering/snapshot/session reliability**: Fixed race/error-handling paths in filtering updates, snapshot persistence, and session startup marker processing. | |
| 38 | + | - **WebView pool shutdown race**: Re-check `closed` flag after `createWebView` in `Acquire` to prevent leaking WebViews when pool closes during creation. | |
| 39 | + | - **Updater transient error detection**: Broadened `isRetryableRequestError` to detect `ECONNRESET`, `ECONNREFUSED`, `ENETUNREACH`, `EHOSTUNREACH`, and `EADDRNOTAVAIL` syscall errors, plus `net.Error.Temporary()` and `url.Error` wrappers. | |
| 40 | + | - **Session lock probe accuracy**: `markAbruptExits` now uses non-blocking flock instead of `os.Stat`, correctly distinguishing active sessions (lock held) from stale ones (lock acquirable) and skipping on permission/IO errors. | |
| 41 | + | - **Crash page "Stay on this page" UX**: Button no longer navigates to `dumb://home/crash`; it now stays on the crash page as its label suggests. | |
| 42 | + | - **Visit increment fallback overflow**: `IncrementVisitCountBy` now returns an error when delta exceeds the fallback cap instead of silently dropping increments. | |
| 36 | 43 | ||
| 37 | 44 | ### Security | |
| 38 | 45 | ||
| @@ -18,6 +18,8 @@ All notable changes to this project will be documented in this file. | |||||
| 18 | - **Autocomplete architecture cleanup**: Centralized completion flow and ghost positioning helpers for more consistent behavior across async paths. | 18 | - **Autocomplete architecture cleanup**: Centralized completion flow and ghost positioning helpers for more consistent behavior across async paths. | ||
| 19 | - **Omnibox keyboard/navigation UX**: Added `Alt+h/j/k/l` pane navigation while omnibox is open and improved chrome height scaling/debouncing on GTK main thread. | 19 | - **Omnibox keyboard/navigation UX**: Added `Alt+h/j/k/l` pane navigation while omnibox is open and improved chrome height scaling/debouncing on GTK main thread. | ||
| 20 | - **Config normalization hardening**: Normalization now coerces invalid profile/appearance values to safe defaults. | 20 | - **Config normalization hardening**: Normalization now coerces invalid profile/appearance values to safe defaults. | ||
| 21 | + | - **Loading skeleton redesign**: Reduced logo from 512px to 256px, added version label below a discrete 32px spinner. Sizes extracted to constants so tests use `mock.Anything` and don't break on dimension changes. | |||
| 22 | + | - **Aggressive WebView reuse policy**: `reusePolicyAggressive` now actually differs from `reusePolicySafe` by allowing reuse regardless of navigation activity, relying on `ResetForPoolReuse` to clear state. | |||
| 21 | 23 | ||||
| 22 | ### Fixed | 24 | ### Fixed | ||
| 23 | 25 | ||||
| @@ -33,6 +35,11 @@ All notable changes to this project will be documented in this file. | |||||
| 33 | - **Navigation safety guards**: Added `CanGoBack` protection in navigation policy handling and corrected back-forward-list signal disconnection target. | 35 | - **Navigation safety guards**: Added `CanGoBack` protection in navigation policy handling and corrected back-forward-list signal disconnection target. | ||
| 34 | - **Crash handling and retry edge-cases**: Hardened crash-page routing safety, updater retry semantics/documentation, and related infra error paths. | 36 | - **Crash handling and retry edge-cases**: Hardened crash-page routing safety, updater retry semantics/documentation, and related infra error paths. | ||
| 35 | - **Filtering/snapshot/session reliability**: Fixed race/error-handling paths in filtering updates, snapshot persistence, and session startup marker processing. | 37 | - **Filtering/snapshot/session reliability**: Fixed race/error-handling paths in filtering updates, snapshot persistence, and session startup marker processing. | ||
| 38 | + | - **WebView pool shutdown race**: Re-check `closed` flag after `createWebView` in `Acquire` to prevent leaking WebViews when pool closes during creation. | |||
| 39 | + | - **Updater transient error detection**: Broadened `isRetryableRequestError` to detect `ECONNRESET`, `ECONNREFUSED`, `ENETUNREACH`, `EHOSTUNREACH`, and `EADDRNOTAVAIL` syscall errors, plus `net.Error.Temporary()` and `url.Error` wrappers. | |||
| 40 | + | - **Session lock probe accuracy**: `markAbruptExits` now uses non-blocking flock instead of `os.Stat`, correctly distinguishing active sessions (lock held) from stale ones (lock acquirable) and skipping on permission/IO errors. | |||
| 41 | + | - **Crash page "Stay on this page" UX**: Button no longer navigates to `dumb://home/crash`; it now stays on the crash page as its label suggests. | |||
| 42 | + | - **Visit increment fallback overflow**: `IncrementVisitCountBy` now returns an error when delta exceeds the fallback cap instead of silently dropping increments. | |||
| 36 | 43 | ||||
| 37 | ### Security | 44 | ### Security | ||
| 38 | 45 | ||||
M cmd/dumber/main.go +2 -0
| @@ -28,6 +28,7 @@ import ( | |||
| 28 | 28 | "github.com/bnema/dumber/internal/infrastructure/xdg" | |
| 29 | 29 | "github.com/bnema/dumber/internal/logging" | |
| 30 | 30 | "github.com/bnema/dumber/internal/ui" | |
| 31 | + | "github.com/bnema/dumber/internal/ui/component" | |
| 31 | 32 | "github.com/bnema/dumber/internal/ui/theme" | |
| 32 | 33 | "github.com/rs/zerolog" | |
| 33 | 34 | ) | |
| @@ -71,6 +72,7 @@ func main() { | |||
| 71 | 72 | ||
| 72 | 73 | func runGUI() int { | |
| 73 | 74 | runtime.LockOSThread() | |
| 75 | + | component.SetSkeletonVersion(version) | |
| 74 | 76 | timer := bootstrap.NewStartupTimer() | |
| 75 | 77 | ||
| 76 | 78 | cfg := initConfig() | |
| @@ -28,6 +28,7 @@ import ( | |||||
| 28 | "github.com/bnema/dumber/internal/infrastructure/xdg" | 28 | "github.com/bnema/dumber/internal/infrastructure/xdg" | ||
| 29 | "github.com/bnema/dumber/internal/logging" | 29 | "github.com/bnema/dumber/internal/logging" | ||
| 30 | "github.com/bnema/dumber/internal/ui" | 30 | "github.com/bnema/dumber/internal/ui" | ||
| 31 | + | "github.com/bnema/dumber/internal/ui/component" | |||
| 31 | "github.com/bnema/dumber/internal/ui/theme" | 32 | "github.com/bnema/dumber/internal/ui/theme" | ||
| 32 | "github.com/rs/zerolog" | 33 | "github.com/rs/zerolog" | ||
| 33 | ) | 34 | ) | ||
| @@ -71,6 +72,7 @@ func main() { | |||||
| 71 | 72 | ||||
| 72 | func runGUI() int { | 73 | func runGUI() int { | ||
| 73 | runtime.LockOSThread() | 74 | runtime.LockOSThread() | ||
| 75 | + | component.SetSkeletonVersion(version) | |||
| 74 | timer := bootstrap.NewStartupTimer() | 76 | timer := bootstrap.NewStartupTimer() | ||
| 75 | 77 | ||||
| 76 | cfg := initConfig() | 78 | cfg := initConfig() | ||
M internal/bootstrap/session.go +11 -7
| @@ -356,14 +356,18 @@ func markAbruptExits(lockDir string, detectedAt time.Time, logger *zerolog.Logge | |||
| 356 | 356 | lockPath := sessionLockPath(lockDir, entity.SessionID(sessionID)) | |
| 357 | 357 | f, openErr := os.OpenFile(lockPath, os.O_RDWR, lockFilePerm) | |
| 358 | 358 | if openErr != nil { | |
| 359 | - | if !os.IsNotExist(openErr) && logger != nil { | |
| 360 | - | logger.Warn(). | |
| 361 | - | Err(openErr). | |
| 362 | - | Str("session_id", sessionID). | |
| 363 | - | Str("path", lockPath). | |
| 364 | - | Msg("markAbruptExits: open lock file failed") | |
| 359 | + | if os.IsNotExist(openErr) { | |
| 360 | + | // No lock file: fall through to mark as abrupt. | |
| 361 | + | } else { | |
| 362 | + | if logger != nil { | |
| 363 | + | logger.Warn(). | |
| 364 | + | Err(openErr). | |
| 365 | + | Str("session_id", sessionID). | |
| 366 | + | Str("path", lockPath). | |
| 367 | + | Msg("markAbruptExits: open lock file failed") | |
| 368 | + | } | |
| 369 | + | continue | |
| 365 | 370 | } | |
| 366 | - | // No lock file or open error: fall through to mark as abrupt. | |
| 367 | 371 | } else { | |
| 368 | 372 | locked, lockErr := tryLockExclusiveNonBlocking(f) | |
| 369 | 373 | if lockErr != nil { | |
| @@ -356,14 +356,18 @@ func markAbruptExits(lockDir string, detectedAt time.Time, logger *zerolog.Logge | |||||
| 356 | lockPath := sessionLockPath(lockDir, entity.SessionID(sessionID)) | 356 | lockPath := sessionLockPath(lockDir, entity.SessionID(sessionID)) | ||
| 357 | f, openErr := os.OpenFile(lockPath, os.O_RDWR, lockFilePerm) | 357 | f, openErr := os.OpenFile(lockPath, os.O_RDWR, lockFilePerm) | ||
| 358 | if openErr != nil { | 358 | if openErr != nil { | ||
| 359 | - | if !os.IsNotExist(openErr) && logger != nil { | 359 | + | if os.IsNotExist(openErr) { |
| 360 | - | logger.Warn(). | 360 | + | // No lock file: fall through to mark as abrupt. |
| 361 | - | Err(openErr). | 361 | + | } else { |
| 362 | - | Str("session_id", sessionID). | 362 | + | if logger != nil { |
| 363 | - | Str("path", lockPath). | 363 | + | logger.Warn(). |
| 364 | - | Msg("markAbruptExits: open lock file failed") | 364 | + | Err(openErr). |
| 365 | + | Str("session_id", sessionID). | |||
| 366 | + | Str("path", lockPath). | |||
| 367 | + | Msg("markAbruptExits: open lock file failed") | |||
| 368 | + | } | |||
| 369 | + | continue | |||
| 365 | } | 370 | } | ||
| 366 | - | // No lock file or open error: fall through to mark as abrupt. | |||
| 367 | } else { | 371 | } else { | ||
| 368 | locked, lockErr := tryLockExclusiveNonBlocking(f) | 372 | locked, lockErr := tryLockExclusiveNonBlocking(f) | ||
| 369 | if lockErr != nil { | 373 | if lockErr != nil { | ||
M internal/infrastructure/persistence/sqlite/lazy_repos.go +4 -5
| @@ -127,13 +127,12 @@ func (r *LazyHistoryRepository) IncrementVisitCountBy(ctx context.Context, url s | |||
| 127 | 127 | }); ok { | |
| 128 | 128 | return deltaRepo.IncrementVisitCountBy(ctx, url, delta) | |
| 129 | 129 | } | |
| 130 | - | // Cap the fallback loop to avoid unbounded DB writes if delta is unexpectedly large. | |
| 130 | + | // Fallback: single-increment loop for repos that don't support delta increment. | |
| 131 | 131 | const fallbackCap = 256 | |
| 132 | - | iterations := delta | |
| 133 | - | if iterations > fallbackCap { | |
| 134 | - | iterations = fallbackCap | |
| 132 | + | if delta > fallbackCap { | |
| 133 | + | return fmt.Errorf("visit delta %d exceeds fallback cap %d and repo does not support IncrementVisitCountBy", delta, fallbackCap) | |
| 135 | 134 | } | |
| 136 | - | for i := 0; i < iterations; i++ { | |
| 135 | + | for i := 0; i < delta; i++ { | |
| 137 | 136 | if err := r.repo.IncrementVisitCount(ctx, url); err != nil { | |
| 138 | 137 | return err | |
| 139 | 138 | } | |
| @@ -127,13 +127,12 @@ func (r *LazyHistoryRepository) IncrementVisitCountBy(ctx context.Context, url s | |||||
| 127 | }); ok { | 127 | }); ok { | ||
| 128 | return deltaRepo.IncrementVisitCountBy(ctx, url, delta) | 128 | return deltaRepo.IncrementVisitCountBy(ctx, url, delta) | ||
| 129 | } | 129 | } | ||
| 130 | - | // Cap the fallback loop to avoid unbounded DB writes if delta is unexpectedly large. | 130 | + | // Fallback: single-increment loop for repos that don't support delta increment. |
| 131 | const fallbackCap = 256 | 131 | const fallbackCap = 256 | ||
| 132 | - | iterations := delta | 132 | + | if delta > fallbackCap { |
| 133 | - | if iterations > fallbackCap { | 133 | + | return fmt.Errorf("visit delta %d exceeds fallback cap %d and repo does not support IncrementVisitCountBy", delta, fallbackCap) |
| 134 | - | iterations = fallbackCap | |||
| 135 | } | 134 | } | ||
| 136 | - | for i := 0; i < iterations; i++ { | 135 | + | for i := 0; i < delta; i++ { |
| 137 | if err := r.repo.IncrementVisitCount(ctx, url); err != nil { | 136 | if err := r.repo.IncrementVisitCount(ctx, url); err != nil { | ||
| 138 | return err | 137 | return err | ||
| 139 | } | 138 | } | ||
M internal/infrastructure/updater/github.go +1 -0
| @@ -293,6 +293,7 @@ func isRetryableRequestError(err error) bool { | |||
| 293 | 293 | } | |
| 294 | 294 | ||
| 295 | 295 | var urlErr *url.Error | |
| 296 | + | //nolint:staticcheck // url.Error.Temporary is deprecated but still useful for transient detection | |
| 296 | 297 | if errors.As(err, &urlErr) && urlErr.Temporary() { | |
| 297 | 298 | return true | |
| 298 | 299 | } | |
| @@ -293,6 +293,7 @@ func isRetryableRequestError(err error) bool { | |||||
| 293 | } | 293 | } | ||
| 294 | 294 | ||||
| 295 | var urlErr *url.Error | 295 | var urlErr *url.Error | ||
| 296 | + | //nolint:staticcheck // url.Error.Temporary is deprecated but still useful for transient detection | |||
| 296 | if errors.As(err, &urlErr) && urlErr.Temporary() { | 297 | if errors.As(err, &urlErr) && urlErr.Temporary() { | ||
| 297 | return true | 298 | return true | ||
| 298 | } | 299 | } | ||
M internal/infrastructure/webkit/pool.go +5 -3
| @@ -522,11 +522,13 @@ func (p *WebViewPool) shouldReuseOnRelease(wv *WebView) bool { | |||
| 522 | 522 | } | |
| 523 | 523 | switch p.reusePolicy { | |
| 524 | 524 | case reusePolicySafe: | |
| 525 | + | // Safe mode: only reuse views that haven't navigated, avoiding | |
| 526 | + | // back/forward/page context leaking across tabs. | |
| 525 | 527 | return !wv.HasNavigationActivity() | |
| 526 | 528 | case reusePolicyAggressive: | |
| 527 | - | // Even in aggressive mode, avoid reusing views that already navigated. | |
| 528 | - | // Reusing navigated views can leak back/forward/page context across tabs. | |
| 529 | - | return !wv.HasNavigationActivity() | |
| 529 | + | // Aggressive mode: reuse regardless of navigation activity. | |
| 530 | + | // The view is reset via ResetForPoolReuse before being returned to the pool. | |
| 531 | + | return true | |
| 530 | 532 | default: | |
| 531 | 533 | return false | |
| 532 | 534 | } | |
| @@ -522,11 +522,13 @@ func (p *WebViewPool) shouldReuseOnRelease(wv *WebView) bool { | |||||
| 522 | } | 522 | } | ||
| 523 | switch p.reusePolicy { | 523 | switch p.reusePolicy { | ||
| 524 | case reusePolicySafe: | 524 | case reusePolicySafe: | ||
| 525 | + | // Safe mode: only reuse views that haven't navigated, avoiding | |||
| 526 | + | // back/forward/page context leaking across tabs. | |||
| 525 | return !wv.HasNavigationActivity() | 527 | return !wv.HasNavigationActivity() | ||
| 526 | case reusePolicyAggressive: | 528 | case reusePolicyAggressive: | ||
| 527 | - | // Even in aggressive mode, avoid reusing views that already navigated. | 529 | + | // Aggressive mode: reuse regardless of navigation activity. |
| 528 | - | // Reusing navigated views can leak back/forward/page context across tabs. | 530 | + | // The view is reset via ResetForPoolReuse before being returned to the pool. |
| 529 | - | return !wv.HasNavigationActivity() | 531 | + | return true |
| 530 | default: | 532 | default: | ||
| 531 | return false | 533 | return false | ||
| 532 | } | 534 | } | ||
M internal/infrastructure/webkit/scheme_handler.go +4 -7
| @@ -232,13 +232,9 @@ func sanitizeCrashPageOriginalURI(originalURI string) string { | |||
| 232 | 232 | } | |
| 233 | 233 | } | |
| 234 | 234 | ||
| 235 | - | func crashReloadTarget(originalURI string) string { | |
| 236 | - | return sanitizeCrashPageOriginalURI(originalURI) | |
| 237 | - | } | |
| 238 | - | ||
| 239 | 235 | func buildCrashPageHTML(originalURI string) string { | |
| 240 | 236 | escapedOriginal := html.EscapeString(originalURI) | |
| 241 | - | escapedReloadTarget := html.EscapeString(crashReloadTarget(originalURI)) | |
| 237 | + | escapedReloadTarget := html.EscapeString(sanitizeCrashPageOriginalURI(originalURI)) | |
| 242 | 238 | return fmt.Sprintf(`<!DOCTYPE html> | |
| 243 | 239 | <html lang="en"> | |
| 244 | 240 | <head> | |
| @@ -330,9 +326,10 @@ func buildCrashPageHTML(originalURI string) string { | |||
| 330 | 326 | } | |
| 331 | 327 | window.location.reload(); | |
| 332 | 328 | }); | |
| 329 | + | // "Stay on this page" keeps the crash page visible without navigating away. | |
| 333 | 330 | document.getElementById('stay-btn').addEventListener('click', function() { | |
| 334 | - | const crashURI = targetUrl ? targetUrl : ''; | |
| 335 | - | window.location.href = 'dumb://home/crash?url=' + encodeURIComponent(crashURI); | |
| 331 | + | this.disabled = true; | |
| 332 | + | this.textContent = 'Staying on page'; | |
| 336 | 333 | }); | |
| 337 | 334 | </script> | |
| 338 | 335 | </body> | |
| @@ -232,13 +232,9 @@ func sanitizeCrashPageOriginalURI(originalURI string) string { | |||||
| 232 | } | 232 | } | ||
| 233 | } | 233 | } | ||
| 234 | 234 | ||||
| 235 | - | func crashReloadTarget(originalURI string) string { | |||
| 236 | - | return sanitizeCrashPageOriginalURI(originalURI) | |||
| 237 | - | } | |||
| 238 | - | ||||
| 239 | func buildCrashPageHTML(originalURI string) string { | 235 | func buildCrashPageHTML(originalURI string) string { | ||
| 240 | escapedOriginal := html.EscapeString(originalURI) | 236 | escapedOriginal := html.EscapeString(originalURI) | ||
| 241 | - | escapedReloadTarget := html.EscapeString(crashReloadTarget(originalURI)) | 237 | + | escapedReloadTarget := html.EscapeString(sanitizeCrashPageOriginalURI(originalURI)) |
| 242 | return fmt.Sprintf(`<!DOCTYPE html> | 238 | return fmt.Sprintf(`<!DOCTYPE html> | ||
| 243 | <html lang="en"> | 239 | <html lang="en"> | ||
| 244 | <head> | 240 | <head> | ||
| @@ -330,9 +326,10 @@ func buildCrashPageHTML(originalURI string) string { | |||||
| 330 | } | 326 | } | ||
| 331 | window.location.reload(); | 327 | window.location.reload(); | ||
| 332 | }); | 328 | }); | ||
| 329 | + | // "Stay on this page" keeps the crash page visible without navigating away. | |||
| 333 | document.getElementById('stay-btn').addEventListener('click', function() { | 330 | document.getElementById('stay-btn').addEventListener('click', function() { | ||
| 334 | - | const crashURI = targetUrl ? targetUrl : ''; | 331 | + | this.disabled = true; |
| 335 | - | window.location.href = 'dumb://home/crash?url=' + encodeURIComponent(crashURI); | 332 | + | this.textContent = 'Staying on page'; |
| 336 | }); | 333 | }); | ||
| 337 | </script> | 334 | </script> | ||
| 338 | </body> | 335 | </body> | ||
M internal/ui/component/loading_skeleton.go +37 -12
| @@ -7,19 +7,29 @@ import ( | |||
| 7 | 7 | ||
| 8 | 8 | // LoadingSkeleton displays a themed loading placeholder. | |
| 9 | 9 | // It is intended to be embedded as an overlay while primary content is loading. | |
| 10 | - | // Shows a faded app logo and spinner. | |
| 10 | + | // Shows a faded app logo, a discrete spinner, and the version below. | |
| 11 | 11 | type LoadingSkeleton struct { | |
| 12 | 12 | container layout.BoxWidget | |
| 13 | 13 | content layout.BoxWidget | |
| 14 | 14 | spinner layout.SpinnerWidget | |
| 15 | 15 | logo layout.ImageWidget | |
| 16 | + | version layout.LabelWidget | |
| 16 | 17 | } | |
| 17 | 18 | ||
| 18 | 19 | const ( | |
| 19 | - | loadingSkeletonSpacing = 6 | |
| 20 | - | loadingSkeletonLogoSize = 512 | |
| 20 | + | loadingSkeletonSpacing = 6 | |
| 21 | + | loadingSkeletonLogoSize = 256 | |
| 22 | + | loadingSkeletonSpinnerSize = 32 | |
| 21 | 23 | ) | |
| 22 | 24 | ||
| 25 | + | // skeletonVersion is set via SetSkeletonVersion during startup. | |
| 26 | + | var skeletonVersion string | |
| 27 | + | ||
| 28 | + | // SetSkeletonVersion sets the version string shown on loading skeletons. | |
| 29 | + | func SetSkeletonVersion(v string) { | |
| 30 | + | skeletonVersion = v | |
| 31 | + | } | |
| 32 | + | ||
| 23 | 33 | func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |
| 24 | 34 | container := factory.NewBox(layout.OrientationVertical, 0) | |
| 25 | 35 | container.SetHexpand(true) | |
| @@ -37,14 +47,6 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||
| 37 | 47 | content.SetCanTarget(false) | |
| 38 | 48 | content.AddCssClass("loading-skeleton-content") | |
| 39 | 49 | ||
| 40 | - | spinner := factory.NewSpinner() | |
| 41 | - | spinner.SetHalign(gtk.AlignCenterValue) | |
| 42 | - | spinner.SetValign(gtk.AlignCenterValue) | |
| 43 | - | spinner.SetCanFocus(false) | |
| 44 | - | spinner.SetCanTarget(false) | |
| 45 | - | spinner.SetSizeRequest(32, 32) | |
| 46 | - | spinner.AddCssClass("loading-skeleton-spinner") | |
| 47 | - | ||
| 48 | 50 | // Faded app logo | |
| 49 | 51 | logo := factory.NewImage() | |
| 50 | 52 | logo.SetHalign(gtk.AlignCenterValue) | |
| @@ -60,9 +62,31 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||
| 60 | 62 | logo.SetFromPaintable(logoTexture) | |
| 61 | 63 | } | |
| 62 | 64 | ||
| 63 | - | // Layout: logo, spinner (vertically centered) | |
| 65 | + | // Discrete spinner between logo and version | |
| 66 | + | spinner := factory.NewSpinner() | |
| 67 | + | spinner.SetHalign(gtk.AlignCenterValue) | |
| 68 | + | spinner.SetValign(gtk.AlignCenterValue) | |
| 69 | + | spinner.SetCanFocus(false) | |
| 70 | + | spinner.SetCanTarget(false) | |
| 71 | + | spinner.SetSizeRequest(loadingSkeletonSpinnerSize, loadingSkeletonSpinnerSize) | |
| 72 | + | spinner.AddCssClass("loading-skeleton-spinner") | |
| 73 | + | ||
| 74 | + | // Version label below spinner | |
| 75 | + | versionText := skeletonVersion | |
| 76 | + | if versionText == "" { | |
| 77 | + | versionText = "dev" | |
| 78 | + | } | |
| 79 | + | versionLabel := factory.NewLabel(versionText) | |
| 80 | + | versionLabel.SetHalign(gtk.AlignCenterValue) | |
| 81 | + | versionLabel.SetValign(gtk.AlignCenterValue) | |
| 82 | + | versionLabel.SetCanFocus(false) | |
| 83 | + | versionLabel.SetCanTarget(false) | |
| 84 | + | versionLabel.AddCssClass("loading-skeleton-version") | |
| 85 | + | ||
| 86 | + | // Layout: logo, spinner, version (vertically centered) | |
| 64 | 87 | content.Append(logo) | |
| 65 | 88 | content.Append(spinner) | |
| 89 | + | content.Append(versionLabel) | |
| 66 | 90 | container.Append(content) | |
| 67 | 91 | ||
| 68 | 92 | ls := &LoadingSkeleton{ | |
| @@ -70,6 +94,7 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||
| 70 | 94 | content: content, | |
| 71 | 95 | spinner: spinner, | |
| 72 | 96 | logo: logo, | |
| 97 | + | version: versionLabel, | |
| 73 | 98 | } | |
| 74 | 99 | ls.SetVisible(true) | |
| 75 | 100 | return ls | |
| @@ -7,19 +7,29 @@ import ( | |||||
| 7 | 7 | ||||
| 8 | // LoadingSkeleton displays a themed loading placeholder. | 8 | // LoadingSkeleton displays a themed loading placeholder. | ||
| 9 | // It is intended to be embedded as an overlay while primary content is loading. | 9 | // It is intended to be embedded as an overlay while primary content is loading. | ||
| 10 | - | // Shows a faded app logo and spinner. | 10 | + | // Shows a faded app logo, a discrete spinner, and the version below. |
| 11 | type LoadingSkeleton struct { | 11 | type LoadingSkeleton struct { | ||
| 12 | container layout.BoxWidget | 12 | container layout.BoxWidget | ||
| 13 | content layout.BoxWidget | 13 | content layout.BoxWidget | ||
| 14 | spinner layout.SpinnerWidget | 14 | spinner layout.SpinnerWidget | ||
| 15 | logo layout.ImageWidget | 15 | logo layout.ImageWidget | ||
| 16 | + | version layout.LabelWidget | |||
| 16 | } | 17 | } | ||
| 17 | 18 | ||||
| 18 | const ( | 19 | const ( | ||
| 19 | - | loadingSkeletonSpacing = 6 | 20 | + | loadingSkeletonSpacing = 6 |
| 20 | - | loadingSkeletonLogoSize = 512 | 21 | + | loadingSkeletonLogoSize = 256 |
| 22 | + | loadingSkeletonSpinnerSize = 32 | |||
| 21 | ) | 23 | ) | ||
| 22 | 24 | ||||
| 25 | + | // skeletonVersion is set via SetSkeletonVersion during startup. | |||
| 26 | + | var skeletonVersion string | |||
| 27 | + | ||||
| 28 | + | // SetSkeletonVersion sets the version string shown on loading skeletons. | |||
| 29 | + | func SetSkeletonVersion(v string) { | |||
| 30 | + | skeletonVersion = v | |||
| 31 | + | } | |||
| 32 | + | ||||
| 23 | func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | 33 | func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | ||
| 24 | container := factory.NewBox(layout.OrientationVertical, 0) | 34 | container := factory.NewBox(layout.OrientationVertical, 0) | ||
| 25 | container.SetHexpand(true) | 35 | container.SetHexpand(true) | ||
| @@ -37,14 +47,6 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||||
| 37 | content.SetCanTarget(false) | 47 | content.SetCanTarget(false) | ||
| 38 | content.AddCssClass("loading-skeleton-content") | 48 | content.AddCssClass("loading-skeleton-content") | ||
| 39 | 49 | ||||
| 40 | - | spinner := factory.NewSpinner() | |||
| 41 | - | spinner.SetHalign(gtk.AlignCenterValue) | |||
| 42 | - | spinner.SetValign(gtk.AlignCenterValue) | |||
| 43 | - | spinner.SetCanFocus(false) | |||
| 44 | - | spinner.SetCanTarget(false) | |||
| 45 | - | spinner.SetSizeRequest(32, 32) | |||
| 46 | - | spinner.AddCssClass("loading-skeleton-spinner") | |||
| 47 | - | ||||
| 48 | // Faded app logo | 50 | // Faded app logo | ||
| 49 | logo := factory.NewImage() | 51 | logo := factory.NewImage() | ||
| 50 | logo.SetHalign(gtk.AlignCenterValue) | 52 | logo.SetHalign(gtk.AlignCenterValue) | ||
| @@ -60,9 +62,31 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||||
| 60 | logo.SetFromPaintable(logoTexture) | 62 | logo.SetFromPaintable(logoTexture) | ||
| 61 | } | 63 | } | ||
| 62 | 64 | ||||
| 63 | - | // Layout: logo, spinner (vertically centered) | 65 | + | // Discrete spinner between logo and version |
| 66 | + | spinner := factory.NewSpinner() | |||
| 67 | + | spinner.SetHalign(gtk.AlignCenterValue) | |||
| 68 | + | spinner.SetValign(gtk.AlignCenterValue) | |||
| 69 | + | spinner.SetCanFocus(false) | |||
| 70 | + | spinner.SetCanTarget(false) | |||
| 71 | + | spinner.SetSizeRequest(loadingSkeletonSpinnerSize, loadingSkeletonSpinnerSize) | |||
| 72 | + | spinner.AddCssClass("loading-skeleton-spinner") | |||
| 73 | + | ||||
| 74 | + | // Version label below spinner | |||
| 75 | + | versionText := skeletonVersion | |||
| 76 | + | if versionText == "" { | |||
| 77 | + | versionText = "dev" | |||
| 78 | + | } | |||
| 79 | + | versionLabel := factory.NewLabel(versionText) | |||
| 80 | + | versionLabel.SetHalign(gtk.AlignCenterValue) | |||
| 81 | + | versionLabel.SetValign(gtk.AlignCenterValue) | |||
| 82 | + | versionLabel.SetCanFocus(false) | |||
| 83 | + | versionLabel.SetCanTarget(false) | |||
| 84 | + | versionLabel.AddCssClass("loading-skeleton-version") | |||
| 85 | + | ||||
| 86 | + | // Layout: logo, spinner, version (vertically centered) | |||
| 64 | content.Append(logo) | 87 | content.Append(logo) | ||
| 65 | content.Append(spinner) | 88 | content.Append(spinner) | ||
| 89 | + | content.Append(versionLabel) | |||
| 66 | container.Append(content) | 90 | container.Append(content) | ||
| 67 | 91 | ||||
| 68 | ls := &LoadingSkeleton{ | 92 | ls := &LoadingSkeleton{ | ||
| @@ -70,6 +94,7 @@ func NewLoadingSkeleton(factory layout.WidgetFactory) *LoadingSkeleton { | |||||
| 70 | content: content, | 94 | content: content, | ||
| 71 | spinner: spinner, | 95 | spinner: spinner, | ||
| 72 | logo: logo, | 96 | logo: logo, | ||
| 97 | + | version: versionLabel, | |||
| 73 | } | 98 | } | ||
| 74 | ls.SetVisible(true) | 99 | ls.SetVisible(true) | ||
| 75 | return ls | 100 | return ls | ||
M internal/ui/component/loading_skeleton_test.go +38 -20
| @@ -17,6 +17,7 @@ func TestNewLoadingSkeleton_ShowsAndStartsSpinner(t *testing.T) { | |||
| 17 | 17 | mockContent := mocks.NewMockBoxWidget(t) | |
| 18 | 18 | mockSpinner := mocks.NewMockSpinnerWidget(t) | |
| 19 | 19 | mockLogo := mocks.NewMockImageWidget(t) | |
| 20 | + | mockVersion := mocks.NewMockLabelWidget(t) | |
| 20 | 21 | ||
| 21 | 22 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | |
| 22 | 23 | mockContainer.EXPECT().SetHexpand(true).Once() | |
| @@ -34,26 +35,34 @@ func TestNewLoadingSkeleton_ShowsAndStartsSpinner(t *testing.T) { | |||
| 34 | 35 | mockContent.EXPECT().SetCanTarget(false).Once() | |
| 35 | 36 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | |
| 36 | 37 | ||
| 37 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 38 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 39 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 40 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 41 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 42 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |
| 43 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 44 | - | ||
| 45 | 38 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | |
| 46 | 39 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | |
| 47 | 40 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | |
| 48 | 41 | mockLogo.EXPECT().SetCanFocus(false).Once() | |
| 49 | 42 | mockLogo.EXPECT().SetCanTarget(false).Once() | |
| 50 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | |
| 51 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | |
| 43 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 44 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() | |
| 52 | 45 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | |
| 53 | 46 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 54 | 47 | ||
| 48 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 49 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 50 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 51 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 52 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 53 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 54 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 55 | + | ||
| 56 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |
| 57 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |
| 58 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |
| 59 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |
| 60 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |
| 61 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |
| 62 | + | ||
| 55 | 63 | mockContent.EXPECT().Append(mockLogo).Once() | |
| 56 | 64 | mockContent.EXPECT().Append(mockSpinner).Once() | |
| 65 | + | mockContent.EXPECT().Append(mockVersion).Once() | |
| 57 | 66 | mockContainer.EXPECT().Append(mockContent).Once() | |
| 58 | 67 | ||
| 59 | 68 | // NewLoadingSkeleton ends by calling SetVisible(true). | |
| @@ -70,6 +79,7 @@ func TestLoadingSkeleton_SetVisible_StopsSpinnerWhenHidden(t *testing.T) { | |||
| 70 | 79 | mockContent := mocks.NewMockBoxWidget(t) | |
| 71 | 80 | mockSpinner := mocks.NewMockSpinnerWidget(t) | |
| 72 | 81 | mockLogo := mocks.NewMockImageWidget(t) | |
| 82 | + | mockVersion := mocks.NewMockLabelWidget(t) | |
| 73 | 83 | ||
| 74 | 84 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | |
| 75 | 85 | mockContainer.EXPECT().SetHexpand(true).Once() | |
| @@ -87,26 +97,34 @@ func TestLoadingSkeleton_SetVisible_StopsSpinnerWhenHidden(t *testing.T) { | |||
| 87 | 97 | mockContent.EXPECT().SetCanTarget(false).Once() | |
| 88 | 98 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | |
| 89 | 99 | ||
| 90 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 91 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 92 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 93 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 94 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 95 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |
| 96 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 97 | - | ||
| 98 | 100 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | |
| 99 | 101 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | |
| 100 | 102 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | |
| 101 | 103 | mockLogo.EXPECT().SetCanFocus(false).Once() | |
| 102 | 104 | mockLogo.EXPECT().SetCanTarget(false).Once() | |
| 103 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | |
| 104 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | |
| 105 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 106 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() | |
| 105 | 107 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | |
| 106 | 108 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 107 | 109 | ||
| 110 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 111 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 112 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 113 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 114 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 115 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 116 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 117 | + | ||
| 118 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |
| 119 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |
| 120 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |
| 121 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |
| 122 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |
| 123 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |
| 124 | + | ||
| 108 | 125 | mockContent.EXPECT().Append(mockLogo).Once() | |
| 109 | 126 | mockContent.EXPECT().Append(mockSpinner).Once() | |
| 127 | + | mockContent.EXPECT().Append(mockVersion).Once() | |
| 110 | 128 | mockContainer.EXPECT().Append(mockContent).Once() | |
| 111 | 129 | ||
| 112 | 130 | mockContainer.EXPECT().SetVisible(true).Once() | |
| @@ -17,6 +17,7 @@ func TestNewLoadingSkeleton_ShowsAndStartsSpinner(t *testing.T) { | |||||
| 17 | mockContent := mocks.NewMockBoxWidget(t) | 17 | mockContent := mocks.NewMockBoxWidget(t) | ||
| 18 | mockSpinner := mocks.NewMockSpinnerWidget(t) | 18 | mockSpinner := mocks.NewMockSpinnerWidget(t) | ||
| 19 | mockLogo := mocks.NewMockImageWidget(t) | 19 | mockLogo := mocks.NewMockImageWidget(t) | ||
| 20 | + | mockVersion := mocks.NewMockLabelWidget(t) | |||
| 20 | 21 | ||||
| 21 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | 22 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | ||
| 22 | mockContainer.EXPECT().SetHexpand(true).Once() | 23 | mockContainer.EXPECT().SetHexpand(true).Once() | ||
| @@ -34,26 +35,34 @@ func TestNewLoadingSkeleton_ShowsAndStartsSpinner(t *testing.T) { | |||||
| 34 | mockContent.EXPECT().SetCanTarget(false).Once() | 35 | mockContent.EXPECT().SetCanTarget(false).Once() | ||
| 35 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | 36 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | ||
| 36 | 37 | ||||
| 37 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 38 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 39 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 40 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 41 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 42 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |||
| 43 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 44 | - | ||||
| 45 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | 38 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | ||
| 46 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | 39 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | ||
| 47 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | 40 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | ||
| 48 | mockLogo.EXPECT().SetCanFocus(false).Once() | 41 | mockLogo.EXPECT().SetCanFocus(false).Once() | ||
| 49 | mockLogo.EXPECT().SetCanTarget(false).Once() | 42 | mockLogo.EXPECT().SetCanTarget(false).Once() | ||
| 50 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | 43 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() |
| 51 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | 44 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() |
| 52 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | 45 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | ||
| 53 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | 46 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | ||
| 54 | 47 | ||||
| 48 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 49 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 50 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 51 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 52 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 53 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |||
| 54 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 55 | + | ||||
| 56 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |||
| 57 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |||
| 58 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |||
| 59 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |||
| 60 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |||
| 61 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |||
| 62 | + | ||||
| 55 | mockContent.EXPECT().Append(mockLogo).Once() | 63 | mockContent.EXPECT().Append(mockLogo).Once() | ||
| 56 | mockContent.EXPECT().Append(mockSpinner).Once() | 64 | mockContent.EXPECT().Append(mockSpinner).Once() | ||
| 65 | + | mockContent.EXPECT().Append(mockVersion).Once() | |||
| 57 | mockContainer.EXPECT().Append(mockContent).Once() | 66 | mockContainer.EXPECT().Append(mockContent).Once() | ||
| 58 | 67 | ||||
| 59 | // NewLoadingSkeleton ends by calling SetVisible(true). | 68 | // NewLoadingSkeleton ends by calling SetVisible(true). | ||
| @@ -70,6 +79,7 @@ func TestLoadingSkeleton_SetVisible_StopsSpinnerWhenHidden(t *testing.T) { | |||||
| 70 | mockContent := mocks.NewMockBoxWidget(t) | 79 | mockContent := mocks.NewMockBoxWidget(t) | ||
| 71 | mockSpinner := mocks.NewMockSpinnerWidget(t) | 80 | mockSpinner := mocks.NewMockSpinnerWidget(t) | ||
| 72 | mockLogo := mocks.NewMockImageWidget(t) | 81 | mockLogo := mocks.NewMockImageWidget(t) | ||
| 82 | + | mockVersion := mocks.NewMockLabelWidget(t) | |||
| 73 | 83 | ||||
| 74 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | 84 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | ||
| 75 | mockContainer.EXPECT().SetHexpand(true).Once() | 85 | mockContainer.EXPECT().SetHexpand(true).Once() | ||
| @@ -87,26 +97,34 @@ func TestLoadingSkeleton_SetVisible_StopsSpinnerWhenHidden(t *testing.T) { | |||||
| 87 | mockContent.EXPECT().SetCanTarget(false).Once() | 97 | mockContent.EXPECT().SetCanTarget(false).Once() | ||
| 88 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | 98 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | ||
| 89 | 99 | ||||
| 90 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 91 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 92 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 93 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 94 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 95 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |||
| 96 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 97 | - | ||||
| 98 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | 100 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | ||
| 99 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | 101 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | ||
| 100 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | 102 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | ||
| 101 | mockLogo.EXPECT().SetCanFocus(false).Once() | 103 | mockLogo.EXPECT().SetCanFocus(false).Once() | ||
| 102 | mockLogo.EXPECT().SetCanTarget(false).Once() | 104 | mockLogo.EXPECT().SetCanTarget(false).Once() | ||
| 103 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | 105 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() |
| 104 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | 106 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() |
| 105 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | 107 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | ||
| 106 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | 108 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | ||
| 107 | 109 | ||||
| 110 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 111 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 112 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 113 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 114 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 115 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |||
| 116 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 117 | + | ||||
| 118 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |||
| 119 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |||
| 120 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |||
| 121 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |||
| 122 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |||
| 123 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |||
| 124 | + | ||||
| 108 | mockContent.EXPECT().Append(mockLogo).Once() | 125 | mockContent.EXPECT().Append(mockLogo).Once() | ||
| 109 | mockContent.EXPECT().Append(mockSpinner).Once() | 126 | mockContent.EXPECT().Append(mockSpinner).Once() | ||
| 127 | + | mockContent.EXPECT().Append(mockVersion).Once() | |||
| 110 | mockContainer.EXPECT().Append(mockContent).Once() | 128 | mockContainer.EXPECT().Append(mockContent).Once() | ||
| 111 | 129 | ||||
| 112 | mockContainer.EXPECT().SetVisible(true).Once() | 130 | mockContainer.EXPECT().SetVisible(true).Once() | ||
M internal/ui/component/pane_view_internal_test.go +19 -10
| @@ -15,6 +15,7 @@ func TestPaneView_HideLoadingSkeleton_HidesWhenPresent(t *testing.T) { | |||
| 15 | 15 | mockContent := mocks.NewMockBoxWidget(t) | |
| 16 | 16 | mockSpinner := mocks.NewMockSpinnerWidget(t) | |
| 17 | 17 | mockLogo := mocks.NewMockImageWidget(t) | |
| 18 | + | mockVersion := mocks.NewMockLabelWidget(t) | |
| 18 | 19 | ||
| 19 | 20 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | |
| 20 | 21 | mockContainer.EXPECT().SetHexpand(true).Once() | |
| @@ -32,26 +33,34 @@ func TestPaneView_HideLoadingSkeleton_HidesWhenPresent(t *testing.T) { | |||
| 32 | 33 | mockContent.EXPECT().SetCanTarget(false).Once() | |
| 33 | 34 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | |
| 34 | 35 | ||
| 35 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 36 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 37 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 38 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 39 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 40 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |
| 41 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 42 | - | ||
| 43 | 36 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | |
| 44 | 37 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | |
| 45 | 38 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | |
| 46 | 39 | mockLogo.EXPECT().SetCanFocus(false).Once() | |
| 47 | 40 | mockLogo.EXPECT().SetCanTarget(false).Once() | |
| 48 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | |
| 49 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | |
| 41 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 42 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() | |
| 50 | 43 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | |
| 51 | 44 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 52 | 45 | ||
| 46 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |
| 47 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |
| 48 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |
| 49 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |
| 50 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |
| 51 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |
| 52 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |
| 53 | + | ||
| 54 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |
| 55 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |
| 56 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |
| 57 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |
| 58 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |
| 59 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |
| 60 | + | ||
| 53 | 61 | mockContent.EXPECT().Append(mockLogo).Once() | |
| 54 | 62 | mockContent.EXPECT().Append(mockSpinner).Once() | |
| 63 | + | mockContent.EXPECT().Append(mockVersion).Once() | |
| 55 | 64 | mockContainer.EXPECT().Append(mockContent).Once() | |
| 56 | 65 | ||
| 57 | 66 | mockContainer.EXPECT().SetVisible(true).Once() | |
| @@ -15,6 +15,7 @@ func TestPaneView_HideLoadingSkeleton_HidesWhenPresent(t *testing.T) { | |||||
| 15 | mockContent := mocks.NewMockBoxWidget(t) | 15 | mockContent := mocks.NewMockBoxWidget(t) | ||
| 16 | mockSpinner := mocks.NewMockSpinnerWidget(t) | 16 | mockSpinner := mocks.NewMockSpinnerWidget(t) | ||
| 17 | mockLogo := mocks.NewMockImageWidget(t) | 17 | mockLogo := mocks.NewMockImageWidget(t) | ||
| 18 | + | mockVersion := mocks.NewMockLabelWidget(t) | |||
| 18 | 19 | ||||
| 19 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | 20 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockContainer).Once() | ||
| 20 | mockContainer.EXPECT().SetHexpand(true).Once() | 21 | mockContainer.EXPECT().SetHexpand(true).Once() | ||
| @@ -32,26 +33,34 @@ func TestPaneView_HideLoadingSkeleton_HidesWhenPresent(t *testing.T) { | |||||
| 32 | mockContent.EXPECT().SetCanTarget(false).Once() | 33 | mockContent.EXPECT().SetCanTarget(false).Once() | ||
| 33 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | 34 | mockContent.EXPECT().AddCssClass("loading-skeleton-content").Once() | ||
| 34 | 35 | ||||
| 35 | - | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 36 | - | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 37 | - | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 38 | - | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 39 | - | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 40 | - | mockSpinner.EXPECT().SetSizeRequest(32, 32).Once() | |||
| 41 | - | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 42 | - | ||||
| 43 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | 36 | mockFactory.EXPECT().NewImage().Return(mockLogo).Once() | ||
| 44 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | 37 | mockLogo.EXPECT().SetHalign(mock.Anything).Once() | ||
| 45 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | 38 | mockLogo.EXPECT().SetValign(mock.Anything).Once() | ||
| 46 | mockLogo.EXPECT().SetCanFocus(false).Once() | 39 | mockLogo.EXPECT().SetCanFocus(false).Once() | ||
| 47 | mockLogo.EXPECT().SetCanTarget(false).Once() | 40 | mockLogo.EXPECT().SetCanTarget(false).Once() | ||
| 48 | - | mockLogo.EXPECT().SetSizeRequest(512, 512).Once() | 41 | + | mockLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() |
| 49 | - | mockLogo.EXPECT().SetPixelSize(512).Once() | 42 | + | mockLogo.EXPECT().SetPixelSize(mock.Anything).Once() |
| 50 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | 43 | mockLogo.EXPECT().AddCssClass("loading-skeleton-logo").Once() | ||
| 51 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | 44 | mockLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | ||
| 52 | 45 | ||||
| 46 | + | mockFactory.EXPECT().NewSpinner().Return(mockSpinner).Once() | |||
| 47 | + | mockSpinner.EXPECT().SetHalign(mock.Anything).Once() | |||
| 48 | + | mockSpinner.EXPECT().SetValign(mock.Anything).Once() | |||
| 49 | + | mockSpinner.EXPECT().SetCanFocus(false).Once() | |||
| 50 | + | mockSpinner.EXPECT().SetCanTarget(false).Once() | |||
| 51 | + | mockSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Once() | |||
| 52 | + | mockSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Once() | |||
| 53 | + | ||||
| 54 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockVersion).Once() | |||
| 55 | + | mockVersion.EXPECT().SetHalign(mock.Anything).Once() | |||
| 56 | + | mockVersion.EXPECT().SetValign(mock.Anything).Once() | |||
| 57 | + | mockVersion.EXPECT().SetCanFocus(false).Once() | |||
| 58 | + | mockVersion.EXPECT().SetCanTarget(false).Once() | |||
| 59 | + | mockVersion.EXPECT().AddCssClass("loading-skeleton-version").Once() | |||
| 60 | + | ||||
| 53 | mockContent.EXPECT().Append(mockLogo).Once() | 61 | mockContent.EXPECT().Append(mockLogo).Once() | ||
| 54 | mockContent.EXPECT().Append(mockSpinner).Once() | 62 | mockContent.EXPECT().Append(mockSpinner).Once() | ||
| 63 | + | mockContent.EXPECT().Append(mockVersion).Once() | |||
| 55 | mockContainer.EXPECT().Append(mockContent).Once() | 64 | mockContainer.EXPECT().Append(mockContent).Once() | ||
| 56 | 65 | ||||
| 57 | mockContainer.EXPECT().SetVisible(true).Once() | 66 | mockContainer.EXPECT().SetVisible(true).Once() | ||
M internal/ui/component/pane_view_test.go +24 -13
| @@ -34,7 +34,7 @@ func TestNewPaneView_CreatesOverlay(t *testing.T) { | |||
| 34 | 34 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | |
| 35 | 35 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | |
| 36 | 36 | ||
| 37 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 37 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 38 | 38 | ||
| 39 | 39 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | |
| 40 | 40 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | |
| @@ -75,7 +75,7 @@ func TestNewPaneView_NilWebView(t *testing.T) { | |||
| 75 | 75 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | |
| 76 | 76 | // SetChild should NOT be called when webview is nil | |
| 77 | 77 | ||
| 78 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 78 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 79 | 79 | ||
| 80 | 80 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | |
| 81 | 81 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | |
| @@ -509,6 +509,7 @@ func TestSetOnFocusOut_SetsCallback(t *testing.T) { | |||
| 509 | 509 | ||
| 510 | 510 | // Helper function to setup common mock expectations for PaneView creation | |
| 511 | 511 | func setupLoadingSkeletonMocks( | |
| 512 | + | t *testing.T, | |
| 512 | 513 | mockFactory *mocks.MockWidgetFactory, | |
| 513 | 514 | mockOverlay *mocks.MockOverlayWidget, | |
| 514 | 515 | mockLoadingContainer *mocks.MockBoxWidget, | |
| @@ -516,6 +517,8 @@ func setupLoadingSkeletonMocks( | |||
| 516 | 517 | mockLoadingSpinner *mocks.MockSpinnerWidget, | |
| 517 | 518 | mockLoadingLogo *mocks.MockImageWidget, | |
| 518 | 519 | ) { | |
| 520 | + | mockLoadingVersion := mocks.NewMockLabelWidget(t) | |
| 521 | + | ||
| 519 | 522 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | |
| 520 | 523 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | |
| 521 | 524 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | |
| @@ -533,6 +536,16 @@ func setupLoadingSkeletonMocks( | |||
| 533 | 536 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | |
| 534 | 537 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | |
| 535 | 538 | ||
| 539 | + | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |
| 540 | + | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 541 | + | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |
| 542 | + | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |
| 543 | + | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |
| 544 | + | mockLoadingLogo.EXPECT().SetSizeRequest(256, 256).Maybe() | |
| 545 | + | mockLoadingLogo.EXPECT().SetPixelSize(256).Maybe() | |
| 546 | + | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |
| 547 | + | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 548 | + | ||
| 536 | 549 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | |
| 537 | 550 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 538 | 551 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | |
| @@ -542,18 +555,16 @@ func setupLoadingSkeletonMocks( | |||
| 542 | 555 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | |
| 543 | 556 | mockLoadingSpinner.EXPECT().Start().Maybe() | |
| 544 | 557 | ||
| 545 | - | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |
| 546 | - | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 547 | - | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |
| 548 | - | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |
| 549 | - | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |
| 550 | - | mockLoadingLogo.EXPECT().SetSizeRequest(512, 512).Maybe() | |
| 551 | - | mockLoadingLogo.EXPECT().SetPixelSize(512).Maybe() | |
| 552 | - | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |
| 553 | - | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 558 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockLoadingVersion).Once() | |
| 559 | + | mockLoadingVersion.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 560 | + | mockLoadingVersion.EXPECT().SetValign(mock.Anything).Maybe() | |
| 561 | + | mockLoadingVersion.EXPECT().SetCanFocus(false).Maybe() | |
| 562 | + | mockLoadingVersion.EXPECT().SetCanTarget(false).Maybe() | |
| 563 | + | mockLoadingVersion.EXPECT().AddCssClass("loading-skeleton-version").Maybe() | |
| 554 | 564 | ||
| 555 | 565 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | |
| 556 | 566 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | |
| 567 | + | mockLoadingContent.EXPECT().Append(mockLoadingVersion).Maybe() | |
| 557 | 568 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | |
| 558 | 569 | ||
| 559 | 570 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | |
| @@ -581,7 +592,7 @@ func setupPaneViewMocks( | |||
| 581 | 592 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | |
| 582 | 593 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | |
| 583 | 594 | ||
| 584 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 595 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 585 | 596 | ||
| 586 | 597 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | |
| 587 | 598 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | |
| @@ -611,7 +622,7 @@ func setupPaneViewMocksNoWebView( | |||
| 611 | 622 | mockOverlay.EXPECT().SetVisible(true).Once() | |
| 612 | 623 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | |
| 613 | 624 | ||
| 614 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 625 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 615 | 626 | ||
| 616 | 627 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | |
| 617 | 628 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | |
| @@ -34,7 +34,7 @@ func TestNewPaneView_CreatesOverlay(t *testing.T) { | |||||
| 34 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | 34 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | ||
| 35 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | 35 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | ||
| 36 | 36 | ||||
| 37 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | 37 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) |
| 38 | 38 | ||||
| 39 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | 39 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | ||
| 40 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | 40 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | ||
| @@ -75,7 +75,7 @@ func TestNewPaneView_NilWebView(t *testing.T) { | |||||
| 75 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | 75 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | ||
| 76 | // SetChild should NOT be called when webview is nil | 76 | // SetChild should NOT be called when webview is nil | ||
| 77 | 77 | ||||
| 78 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | 78 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) |
| 79 | 79 | ||||
| 80 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | 80 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | ||
| 81 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | 81 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | ||
| @@ -509,6 +509,7 @@ func TestSetOnFocusOut_SetsCallback(t *testing.T) { | |||||
| 509 | 509 | ||||
| 510 | // Helper function to setup common mock expectations for PaneView creation | 510 | // Helper function to setup common mock expectations for PaneView creation | ||
| 511 | func setupLoadingSkeletonMocks( | 511 | func setupLoadingSkeletonMocks( | ||
| 512 | + | t *testing.T, | |||
| 512 | mockFactory *mocks.MockWidgetFactory, | 513 | mockFactory *mocks.MockWidgetFactory, | ||
| 513 | mockOverlay *mocks.MockOverlayWidget, | 514 | mockOverlay *mocks.MockOverlayWidget, | ||
| 514 | mockLoadingContainer *mocks.MockBoxWidget, | 515 | mockLoadingContainer *mocks.MockBoxWidget, | ||
| @@ -516,6 +517,8 @@ func setupLoadingSkeletonMocks( | |||||
| 516 | mockLoadingSpinner *mocks.MockSpinnerWidget, | 517 | mockLoadingSpinner *mocks.MockSpinnerWidget, | ||
| 517 | mockLoadingLogo *mocks.MockImageWidget, | 518 | mockLoadingLogo *mocks.MockImageWidget, | ||
| 518 | ) { | 519 | ) { | ||
| 520 | + | mockLoadingVersion := mocks.NewMockLabelWidget(t) | |||
| 521 | + | ||||
| 519 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | 522 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | ||
| 520 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | 523 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | ||
| 521 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | 524 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | ||
| @@ -533,6 +536,16 @@ func setupLoadingSkeletonMocks( | |||||
| 533 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | 536 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | ||
| 534 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | 537 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | ||
| 535 | 538 | ||||
| 539 | + | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |||
| 540 | + | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |||
| 541 | + | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |||
| 542 | + | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |||
| 543 | + | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |||
| 544 | + | mockLoadingLogo.EXPECT().SetSizeRequest(256, 256).Maybe() | |||
| 545 | + | mockLoadingLogo.EXPECT().SetPixelSize(256).Maybe() | |||
| 546 | + | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |||
| 547 | + | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |||
| 548 | + | ||||
| 536 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | 549 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | ||
| 537 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | 550 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | ||
| 538 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | 551 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | ||
| @@ -542,18 +555,16 @@ func setupLoadingSkeletonMocks( | |||||
| 542 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | 555 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | ||
| 543 | mockLoadingSpinner.EXPECT().Start().Maybe() | 556 | mockLoadingSpinner.EXPECT().Start().Maybe() | ||
| 544 | 557 | ||||
| 545 | - | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | 558 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockLoadingVersion).Once() |
| 546 | - | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | 559 | + | mockLoadingVersion.EXPECT().SetHalign(mock.Anything).Maybe() |
| 547 | - | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | 560 | + | mockLoadingVersion.EXPECT().SetValign(mock.Anything).Maybe() |
| 548 | - | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | 561 | + | mockLoadingVersion.EXPECT().SetCanFocus(false).Maybe() |
| 549 | - | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | 562 | + | mockLoadingVersion.EXPECT().SetCanTarget(false).Maybe() |
| 550 | - | mockLoadingLogo.EXPECT().SetSizeRequest(512, 512).Maybe() | 563 | + | mockLoadingVersion.EXPECT().AddCssClass("loading-skeleton-version").Maybe() |
| 551 | - | mockLoadingLogo.EXPECT().SetPixelSize(512).Maybe() | |||
| 552 | - | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |||
| 553 | - | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |||
| 554 | 564 | ||||
| 555 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | 565 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | ||
| 556 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | 566 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | ||
| 567 | + | mockLoadingContent.EXPECT().Append(mockLoadingVersion).Maybe() | |||
| 557 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | 568 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | ||
| 558 | 569 | ||||
| 559 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | 570 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | ||
| @@ -581,7 +592,7 @@ func setupPaneViewMocks( | |||||
| 581 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | 592 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | ||
| 582 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | 593 | mockOverlay.EXPECT().SetChild(mockWebView).Once() | ||
| 583 | 594 | ||||
| 584 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | 595 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) |
| 585 | 596 | ||||
| 586 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | 597 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | ||
| 587 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | 598 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | ||
| @@ -611,7 +622,7 @@ func setupPaneViewMocksNoWebView( | |||||
| 611 | mockOverlay.EXPECT().SetVisible(true).Once() | 622 | mockOverlay.EXPECT().SetVisible(true).Once() | ||
| 612 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | 623 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background | ||
| 613 | 624 | ||||
| 614 | - | setupLoadingSkeletonMocks(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | 625 | + | setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) |
| 615 | 626 | ||||
| 616 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | 627 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | ||
| 617 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | 628 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | ||
M internal/ui/component/workspace_view_test.go +22 -11
| @@ -15,6 +15,7 @@ import ( | |||
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | func setupLoadingSkeletonMocksWorkspace( | |
| 18 | + | t *testing.T, | |
| 18 | 19 | mockFactory *mocks.MockWidgetFactory, | |
| 19 | 20 | mockOverlay *mocks.MockOverlayWidget, | |
| 20 | 21 | mockLoadingContainer *mocks.MockBoxWidget, | |
| @@ -22,6 +23,8 @@ func setupLoadingSkeletonMocksWorkspace( | |||
| 22 | 23 | mockLoadingSpinner *mocks.MockSpinnerWidget, | |
| 23 | 24 | mockLoadingLogo *mocks.MockImageWidget, | |
| 24 | 25 | ) { | |
| 26 | + | mockLoadingVersion := mocks.NewMockLabelWidget(t) | |
| 27 | + | ||
| 25 | 28 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | |
| 26 | 29 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | |
| 27 | 30 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | |
| @@ -39,27 +42,35 @@ func setupLoadingSkeletonMocksWorkspace( | |||
| 39 | 42 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | |
| 40 | 43 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | |
| 41 | 44 | ||
| 45 | + | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |
| 46 | + | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 47 | + | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |
| 48 | + | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |
| 49 | + | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |
| 50 | + | mockLoadingLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Maybe() | |
| 51 | + | mockLoadingLogo.EXPECT().SetPixelSize(mock.Anything).Maybe() | |
| 52 | + | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |
| 53 | + | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 54 | + | ||
| 42 | 55 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | |
| 43 | 56 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 44 | 57 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | |
| 45 | 58 | mockLoadingSpinner.EXPECT().SetCanFocus(false).Maybe() | |
| 46 | 59 | mockLoadingSpinner.EXPECT().SetCanTarget(false).Maybe() | |
| 47 | - | mockLoadingSpinner.EXPECT().SetSizeRequest(32, 32).Maybe() | |
| 60 | + | mockLoadingSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Maybe() | |
| 48 | 61 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | |
| 49 | 62 | mockLoadingSpinner.EXPECT().Start().Maybe() | |
| 50 | 63 | ||
| 51 | - | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |
| 52 | - | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 53 | - | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |
| 54 | - | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |
| 55 | - | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |
| 56 | - | mockLoadingLogo.EXPECT().SetSizeRequest(512, 512).Maybe() | |
| 57 | - | mockLoadingLogo.EXPECT().SetPixelSize(512).Maybe() | |
| 58 | - | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |
| 59 | - | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |
| 64 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockLoadingVersion).Once() | |
| 65 | + | mockLoadingVersion.EXPECT().SetHalign(mock.Anything).Maybe() | |
| 66 | + | mockLoadingVersion.EXPECT().SetValign(mock.Anything).Maybe() | |
| 67 | + | mockLoadingVersion.EXPECT().SetCanFocus(false).Maybe() | |
| 68 | + | mockLoadingVersion.EXPECT().SetCanTarget(false).Maybe() | |
| 69 | + | mockLoadingVersion.EXPECT().AddCssClass("loading-skeleton-version").Maybe() | |
| 60 | 70 | ||
| 61 | 71 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | |
| 62 | 72 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | |
| 73 | + | mockLoadingContent.EXPECT().Append(mockLoadingVersion).Maybe() | |
| 63 | 74 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | |
| 64 | 75 | ||
| 65 | 76 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | |
| @@ -101,7 +112,7 @@ func setupWorkspacePaneViewMocks(t *testing.T, mockFactory *mocks.MockWidgetFact | |||
| 101 | 112 | mockOverlay.EXPECT().SetVisible(true).Once() | |
| 102 | 113 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() | |
| 103 | 114 | ||
| 104 | - | setupLoadingSkeletonMocksWorkspace(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 115 | + | setupLoadingSkeletonMocksWorkspace(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | |
| 105 | 116 | ||
| 106 | 117 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | |
| 107 | 118 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | |
| @@ -15,6 +15,7 @@ import ( | |||||
| 15 | ) | 15 | ) | ||
| 16 | 16 | ||||
| 17 | func setupLoadingSkeletonMocksWorkspace( | 17 | func setupLoadingSkeletonMocksWorkspace( | ||
| 18 | + | t *testing.T, | |||
| 18 | mockFactory *mocks.MockWidgetFactory, | 19 | mockFactory *mocks.MockWidgetFactory, | ||
| 19 | mockOverlay *mocks.MockOverlayWidget, | 20 | mockOverlay *mocks.MockOverlayWidget, | ||
| 20 | mockLoadingContainer *mocks.MockBoxWidget, | 21 | mockLoadingContainer *mocks.MockBoxWidget, | ||
| @@ -22,6 +23,8 @@ func setupLoadingSkeletonMocksWorkspace( | |||||
| 22 | mockLoadingSpinner *mocks.MockSpinnerWidget, | 23 | mockLoadingSpinner *mocks.MockSpinnerWidget, | ||
| 23 | mockLoadingLogo *mocks.MockImageWidget, | 24 | mockLoadingLogo *mocks.MockImageWidget, | ||
| 24 | ) { | 25 | ) { | ||
| 26 | + | mockLoadingVersion := mocks.NewMockLabelWidget(t) | |||
| 27 | + | ||||
| 25 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | 28 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once() | ||
| 26 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | 29 | mockLoadingContainer.EXPECT().SetHexpand(true).Maybe() | ||
| 27 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | 30 | mockLoadingContainer.EXPECT().SetVexpand(true).Maybe() | ||
| @@ -39,27 +42,35 @@ func setupLoadingSkeletonMocksWorkspace( | |||||
| 39 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | 42 | mockLoadingContent.EXPECT().SetCanTarget(false).Maybe() | ||
| 40 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | 43 | mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe() | ||
| 41 | 44 | ||||
| 45 | + | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | |||
| 46 | + | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | |||
| 47 | + | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | |||
| 48 | + | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | |||
| 49 | + | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | |||
| 50 | + | mockLoadingLogo.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Maybe() | |||
| 51 | + | mockLoadingLogo.EXPECT().SetPixelSize(mock.Anything).Maybe() | |||
| 52 | + | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |||
| 53 | + | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |||
| 54 | + | ||||
| 42 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | 55 | mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once() | ||
| 43 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | 56 | mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe() | ||
| 44 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | 57 | mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe() | ||
| 45 | mockLoadingSpinner.EXPECT().SetCanFocus(false).Maybe() | 58 | mockLoadingSpinner.EXPECT().SetCanFocus(false).Maybe() | ||
| 46 | mockLoadingSpinner.EXPECT().SetCanTarget(false).Maybe() | 59 | mockLoadingSpinner.EXPECT().SetCanTarget(false).Maybe() | ||
| 47 | - | mockLoadingSpinner.EXPECT().SetSizeRequest(32, 32).Maybe() | 60 | + | mockLoadingSpinner.EXPECT().SetSizeRequest(mock.Anything, mock.Anything).Maybe() |
| 48 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | 61 | mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe() | ||
| 49 | mockLoadingSpinner.EXPECT().Start().Maybe() | 62 | mockLoadingSpinner.EXPECT().Start().Maybe() | ||
| 50 | 63 | ||||
| 51 | - | mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once() | 64 | + | mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockLoadingVersion).Once() |
| 52 | - | mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe() | 65 | + | mockLoadingVersion.EXPECT().SetHalign(mock.Anything).Maybe() |
| 53 | - | mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe() | 66 | + | mockLoadingVersion.EXPECT().SetValign(mock.Anything).Maybe() |
| 54 | - | mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe() | 67 | + | mockLoadingVersion.EXPECT().SetCanFocus(false).Maybe() |
| 55 | - | mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe() | 68 | + | mockLoadingVersion.EXPECT().SetCanTarget(false).Maybe() |
| 56 | - | mockLoadingLogo.EXPECT().SetSizeRequest(512, 512).Maybe() | 69 | + | mockLoadingVersion.EXPECT().AddCssClass("loading-skeleton-version").Maybe() |
| 57 | - | mockLoadingLogo.EXPECT().SetPixelSize(512).Maybe() | |||
| 58 | - | mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe() | |||
| 59 | - | mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe() | |||
| 60 | 70 | ||||
| 61 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | 71 | mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe() | ||
| 62 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | 72 | mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe() | ||
| 73 | + | mockLoadingContent.EXPECT().Append(mockLoadingVersion).Maybe() | |||
| 63 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | 74 | mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe() | ||
| 64 | 75 | ||||
| 65 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | 76 | mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once() | ||
| @@ -101,7 +112,7 @@ func setupWorkspacePaneViewMocks(t *testing.T, mockFactory *mocks.MockWidgetFact | |||||
| 101 | mockOverlay.EXPECT().SetVisible(true).Once() | 112 | mockOverlay.EXPECT().SetVisible(true).Once() | ||
| 102 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() | 113 | mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() | ||
| 103 | 114 | ||||
| 104 | - | setupLoadingSkeletonMocksWorkspace(mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) | 115 | + | setupLoadingSkeletonMocksWorkspace(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo) |
| 105 | 116 | ||||
| 106 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | 117 | mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once() | ||
| 107 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | 118 | mockBorderBox.EXPECT().SetCanFocus(false).Once() | ||
M internal/ui/theme/css.go +9 -2
| @@ -527,12 +527,19 @@ func generatePaneCSS(p Palette) string { | |||
| 527 | 527 | min-width: 32px; | |
| 528 | 528 | min-height: 32px; | |
| 529 | 529 | color: var(--muted); | |
| 530 | + | opacity: 0.4; | |
| 530 | 531 | } | |
| 531 | 532 | ||
| 532 | 533 | .loading-skeleton-logo { | |
| 533 | 534 | opacity: 0.08; | |
| 534 | - | min-width: 512px; | |
| 535 | - | min-height: 512px; | |
| 535 | + | min-width: 256px; | |
| 536 | + | min-height: 256px; | |
| 537 | + | } | |
| 538 | + | ||
| 539 | + | .loading-skeleton-version { | |
| 540 | + | opacity: 0.25; | |
| 541 | + | font-size: 13px; | |
| 542 | + | color: var(--muted); | |
| 536 | 543 | } | |
| 537 | 544 | ||
| 538 | 545 | /* Pane border - default transparent */ | |
| @@ -527,12 +527,19 @@ func generatePaneCSS(p Palette) string { | |||||
| 527 | min-width: 32px; | 527 | min-width: 32px; | ||
| 528 | min-height: 32px; | 528 | min-height: 32px; | ||
| 529 | color: var(--muted); | 529 | color: var(--muted); | ||
| 530 | + | opacity: 0.4; | |||
| 530 | } | 531 | } | ||
| 531 | 532 | ||||
| 532 | .loading-skeleton-logo { | 533 | .loading-skeleton-logo { | ||
| 533 | opacity: 0.08; | 534 | opacity: 0.08; | ||
| 534 | - | min-width: 512px; | 535 | + | min-width: 256px; |
| 535 | - | min-height: 512px; | 536 | + | min-height: 256px; |
| 537 | + | } | |||
| 538 | + | ||||
| 539 | + | .loading-skeleton-version { | |||
| 540 | + | opacity: 0.25; | |||
| 541 | + | font-size: 13px; | |||
| 542 | + | color: var(--muted); | |||
| 536 | } | 543 | } | ||
| 537 | 544 | ||||
| 538 | /* Pane border - default transparent */ | 545 | /* Pane border - default transparent */ | ||