Skip to content

Add AppCtx for shared app-wide services.#1766

Open
xStrom wants to merge 1 commit into
linebender:mainfrom
xStrom:root-ops
Open

Add AppCtx for shared app-wide services.#1766
xStrom wants to merge 1 commit into
linebender:mainfrom
xStrom:root-ops

Conversation

@xStrom
Copy link
Copy Markdown
Member

@xStrom xStrom commented Apr 22, 2026

For some state it makes sense to have only one copy per app. Until now we haven't had a mechanism to store and provide access to such shared app state. Instead we've hacked around it in various ways. Let's look at two examples.

  1. Clipboard access. Deciding what to get from the clipboard, if anything, is a widget level concern. Different widgets might want different formats like plain text, rich text, or images. Some widget might want to implement its own custom in-app copy/paste mechanism for some app data and ignore the platform clipboard. However, the current hack we have is that masonry_winit detects Ctrl+V and replaces the key event with a plain text paste event. This does not allow for the aforementioned variety of paste logic. Widgets need to be able to decide what to do with the key event or they might want to access the clipboard even from a pointer event.

  2. Font database/cache access. This is currently implemented at the RenderRoot level, meaning that font resolution, loading, and caching is duplicated for each root. It works but is needlessly inefficient.

This PR introduces a new AppCtx type that encapsulates these kind of app-wide services. It does require a fair amount of plumbing at the framework level. I think the benefit of synchronous mutable access at the widget level makes it worth it.

Because the plumbing changes are already quite noisy I migrated only the clipboard access in this PR. Font context migration would be follow-up work.

@xStrom xStrom added masonry Issues relating to the Masonry widget layer needs review Needs review before proceeding. labels Apr 22, 2026
@PoignardAzur
Copy link
Copy Markdown
Contributor

PoignardAzur commented Apr 22, 2026

I need to review this more in-depth, but some scattered thoughts:

  • Having some shared state between RenderRoots probably makes sense.
  • I'd like to look for a name other than AppCtx. I think we call too many things across the ecosystem ThingCtx and it's often uninformative.
  • Passing this global context to every render root method and every pass is a massive amount of plumbing, but I guess it's the only design that makes sense.
  • While I can see the point of sharing font data, I'm a little uneasy about injecting a clipboard provider directly into a shared context. I'd like to centralize RenderRoot's inputs and outputs as much as possible; though I take your point that the way we do it is at odd with letting widgets customize their clipboard handling.

@xStrom
Copy link
Copy Markdown
Member Author

xStrom commented Apr 28, 2026

Regarding the name of AppCtx, if you have a better name we can definitely change it.

I'm not sure I understand your point about centralizing RenderRoot's inputs and outputs. Doesn't having a per-app context do just that? What do you mean by centralizing?

@PoignardAzur
Copy link
Copy Markdown
Contributor

Regarding the name of AppCtx, if you have a better name we can definitely change it.

Mhhh... maybe SharedRootState or SharedAppState?

@xStrom
Copy link
Copy Markdown
Member Author

xStrom commented Apr 28, 2026

Something worth noting as well is that one of the benefits of AppCtx is its length, of both the type name and the variable name. Longer names will more easily cause method declarations to be multi-line.

app_ctx: &mut AppCtx
rstate: &mut SharedRootState
sas: &mut SharedAppState

Of those two you provided, if I had to choose I'd go with SharedAppState although I don't prefer it over AppCtx due to the verbosity. Especially because it's a type that will be frequently encountered.

Some other options could include going even shorter like Hub, Env, or Common. Which would all be fine for shortness at least.

@PoignardAzur
Copy link
Copy Markdown
Contributor

(Am currently on vacation, will reply in a day or two.)

@PoignardAzur
Copy link
Copy Markdown
Contributor

(Well, "a day or two" was optimistic, but I haven't forgotten about this.)

Copy link
Copy Markdown
Contributor

@PoignardAzur PoignardAzur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

I have misgivings about the proliferation of context types (in one code path you have a DriverCtx that gives you an AppCtx that gives you two text contexts) and I still think we should come up with a better name than AppCtx, but I don't want to block on naming.

This does seem like a thing we'll want sooner or later, and I don't have any better alternatives.

Comment on lines +374 to +378
/// Creates a new [`AppCtx`] suitable for testing.
pub fn create_app_ctx() -> AppCtx {
let clipboard = SimpleClipboard::new();
AppCtx::new(Box::new(clipboard))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Optional) This function should probably be inlined. (It's used in tests/paint.rs which is a special case, but even there you could copy the function.)

Comment on lines +210 to +212
struct SimpleClipboard {
contents: String,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Optional) I think this type should be called MockClipboard and moved to its own module.

@PoignardAzur
Copy link
Copy Markdown
Contributor

@xStrom Any blocker left?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

masonry Issues relating to the Masonry widget layer needs review Needs review before proceeding.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants