Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Update it whenever you learn something new about the project's patterns, convent
- Comment only to explain non-obvious reasoning or intent.
- Order functions high-level first, utilities last; order types by importance (public API first, private helpers last).
- Prefer directory submodules with `mod.rs` over sibling `foo.rs` submodule files when introducing new submodule trees.
- When splitting large modules, extract low-coupling impl blocks first and preserve existing external imports via local re-exports in the parent module.

## Rust
- Prefer `derive_more` traits (Debug, Deref) over manual implementations.
Expand All @@ -33,14 +34,17 @@ Update it whenever you learn something new about the project's patterns, convent
- For transient UI indicators (hover/focus highlights), derive visibility/target from current resolved state rather than only from enter/exit edge events.
- For context-specific behavior, prefer targeted follow-up evaluation over broad global rule changes that affect unrelated paths.
- When a generic pass applies fallback state, recompute context-specific state immediately afterward for impacted entities.
- For visual side effects derived from state transitions, prefer computing them in the centralized effects/update phase using previous/current state snapshots instead of duplicating eager updates across input and command paths.
- Keep invariant gating at a single layer where practical; avoid repeating identical mode/eligibility checks across caller and callee.
- When an operation must not emit follow-up commands, model it as `Result<()>` and enforce the invariant at the forwarding boundary.
- For internal invariant violations, prefer explicit panics over silent fallback/continue paths.
- When code guarantees an invariant, avoid defensive fallback branches for that path; keep the direct path and fail explicitly if the invariant is violated.
- For purely defensive invariant checks on hot paths, prefer debug-only assertions to avoid unnecessary release-build work.
- For platform-specific window commands, detect shortcuts where aggregated input state is available and keep the actual window mutation in the shell/window abstraction.
- When multiple transient affordances represent the same interaction mode, keep them behind one shared state instead of parallel flags.
- Cache repeated window state requests at the caller when the underlying platform mutation may hop to the main thread or otherwise be non-trivial.
- On macOS, prefer native menu selector wiring for commands that users can remap in App Shortcuts, instead of hardcoded key-chord matching.
- When refactoring eventful flows, extract pure target/decision helpers first and keep side-effect dispatch ordering unchanged until tests lock transition semantics.

## Testing
- Don't add tests unless explicitly asked.
Expand Down
32 changes: 23 additions & 9 deletions desktop/src/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use massive_shell::{ApplicationContext, FontManager, Scene, ShellEvent};
use massive_shell::{AsyncWindowRenderer, ShellWindow};

use crate::DesktopEnvironment;
use crate::desktop_system::{DesktopCommand, DesktopSystem, ProjectCommand};
use crate::desktop_system::{
DesktopCommand, DesktopSystem, ProjectCommand, TransactionEffectsMode,
};
use crate::event_sourcing::Transaction;
use crate::instance_manager::{InstanceManager, ViewPath};
use crate::projects::{
Expand Down Expand Up @@ -125,10 +127,9 @@ impl Desktop {
desktop_groups_transaction + project_setup_transaction + primary_view_transaction,
&scene,
&mut instance_manager,
TransactionEffectsMode::Setup,
)?;

system.update_effects(false, true)?;

Ok(Self {
scene,
renderer,
Expand Down Expand Up @@ -163,16 +164,22 @@ impl Desktop {
&self.instance_manager,
self.renderer.geometry(),
)?;
let cursor_visible = self.system.cursor_visible();
let cursor_visible = self.system.is_cursor_visible();
if self.cursor_visible != cursor_visible {
self.window.set_cursor_visible(cursor_visible);
self.cursor_visible = cursor_visible;
}
self.system
.transact(cmd, &self.scene, &mut self.instance_manager)?;

let allow_camera_movements = !input_event.any_buttons_pressed();
self.system.update_effects(true, allow_camera_movements)?;
.transact(
cmd,
&self.scene,
&mut self.instance_manager,
if input_event.any_buttons_pressed() {
TransactionEffectsMode::CameraLocked.into()
} else {
None
},
)?;
}

self.renderer.resize_redraw(&window_event)?;
Expand All @@ -190,7 +197,12 @@ impl Desktop {
if self.system.is_present(&instance_id) {
// Did it end on its own? -> Act as the user ended it.
// Robustness: This should probably handled differently.
self.system.transact(DesktopCommand::StopInstance(instance_id), &self.scene, &mut self.instance_manager)?;
self.system.transact(
DesktopCommand::StopInstance(instance_id),
&self.scene,
&mut self.instance_manager,
None,
)?;
}

// Feature: Display the error to the user?
Expand Down Expand Up @@ -234,13 +246,15 @@ impl Desktop {
DesktopCommand::PresentView(instance, info),
&self.scene,
&mut self.instance_manager,
None,
)?;
}
InstanceCommand::DestroyView(id, collector) => {
self.system.transact(
DesktopCommand::HideView((instance, id).into()),
&self.scene,
&mut self.instance_manager,
None,
)?;
self.instance_manager.remove_view((instance, id).into());
// Feature: Don't push the remaining changes immediately and fade the remaining
Expand Down
Loading
Loading