Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

desktop: Add Controls > Step Once, for frame-by-frame stepping #18500

Merged
merged 2 commits into from
Nov 14, 2024
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
53 changes: 47 additions & 6 deletions core/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ type Log = Box<dyn LogBackend>;
type Ui = Box<dyn UiBackend>;
type Video = Box<dyn VideoBackend>;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum RunState {
Playing,
Suspended,
Stepping,
}

pub struct Player {
/// The version of the player we're emulating.
///
Expand All @@ -293,7 +300,7 @@ pub struct Player {

swf: Arc<SwfMovie>,

is_playing: bool,
run_state: RunState,
needs_render: bool,

renderer: Renderer,
Expand Down Expand Up @@ -522,6 +529,12 @@ impl Player {
if self.time_offset > 0 {
self.frame_accumulator -= self.time_offset as f64;
}

// If we are stepping a single frame, immediately suspend ourselves.
if self.run_state == RunState::Stepping {
self.set_run_state(RunState::Suspended);
break;
}
}

// Now that we're done running code,
Expand Down Expand Up @@ -583,7 +596,10 @@ impl Player {
}

pub fn is_playing(&self) -> bool {
self.is_playing
match self.run_state {
RunState::Playing | RunState::Stepping => true,
RunState::Suspended => false,
}
}

pub fn mouse_in_stage(&self) -> bool {
Expand Down Expand Up @@ -850,14 +866,35 @@ impl Player {
}
}

pub fn set_is_playing(&mut self, v: bool) {
if v {
fn set_run_state(&mut self, state: RunState) {
let play_audio = match state {
RunState::Playing => true,
RunState::Suspended => false,
// Do not run audio when stepping frame-by-frame,
// to avoid unpleasant short bursts of sound.
RunState::Stepping => false,
};

if play_audio {
// Allow auto-play after user gesture for web backends.
self.audio.play();
} else {
self.audio.pause();
}
self.is_playing = v;

self.run_state = state;
}

pub fn set_is_playing(&mut self, v: bool) {
self.set_run_state(if v {
RunState::Playing
} else {
RunState::Suspended
});
}

pub fn suspend_after_next_frame(&mut self) {
self.set_run_state(RunState::Stepping);
}

pub fn needs_render(&self) -> bool {
Expand Down Expand Up @@ -2838,7 +2875,11 @@ impl PlayerBuilder {
instance_counter: 0,
player_version,
player_runtime: self.player_runtime,
is_playing: self.autoplay,
run_state: if self.autoplay {
RunState::Playing
} else {
RunState::Suspended
},
needs_render: true,
self_reference: self_ref.clone(),
load_behavior: self.load_behavior,
Expand Down
1 change: 1 addition & 0 deletions desktop/assets/texts/en-US/main_menu.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ file-menu-exit = Exit
controls-menu = Controls
controls-menu-suspend = Suspend
controls-menu-resume = Resume
controls-menu-step-once = Step Once
controls-menu-volume = Volume controls

help-menu = Help
Expand Down
80 changes: 61 additions & 19 deletions desktop/src/gui/menu_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ impl MenuBar {
const SHORTCUT_OPEN_ADVANCED: KeyboardShortcut =
KeyboardShortcut::new(Modifiers::COMMAND.plus(Modifiers::SHIFT), Key::O);
const SHORTCUT_PAUSE: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::P);
const SHORTCUT_STEP: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Space);
const SHORTCUT_QUIT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Q);

pub fn new(
Expand Down Expand Up @@ -61,11 +62,18 @@ impl MenuBar {
if egui_ctx.input_mut(|input| input.consume_shortcut(&Self::SHORTCUT_QUIT)) {
self.request_exit();
}
if egui_ctx.input_mut(|input| input.consume_shortcut(&Self::SHORTCUT_PAUSE)) {
if let Some(player) = &mut player {
player.set_is_playing(!player.is_playing());

if let Some(player) = &mut player {
let playing = player.is_playing();
if egui_ctx.input_mut(|input| input.consume_shortcut(&Self::SHORTCUT_PAUSE)) {
player.set_is_playing(!playing);
}
if !playing && egui_ctx.input_mut(|input| input.consume_shortcut(&Self::SHORTCUT_STEP))
{
player.suspend_after_next_frame();
}
}

let mut fullscreen_pressed =
egui_ctx.input_mut(|input| input.consume_shortcut(&Self::SHORTCUT_FULLSCREEN));
if cfg!(windows) && !fullscreen_pressed {
Expand All @@ -92,22 +100,7 @@ impl MenuBar {
menu::bar(ui, |ui| {
self.file_menu(locale, ui, dialogs, player.is_some());
self.view_menu(locale, ui, &mut player);

menu::menu_button(ui, text(locale, "controls-menu"), |ui| {
ui.add_enabled_ui(player.is_some(), |ui| {
let playing = player.as_ref().map(|p| p.is_playing()).unwrap_or_default();
if Button::new(text(locale, if playing { "controls-menu-suspend" } else { "controls-menu-resume" })).shortcut_text(ui.ctx().format_shortcut(&Self::SHORTCUT_PAUSE)).ui(ui).clicked() {
ui.close_menu();
if let Some(player) = &mut player {
player.set_is_playing(!player.is_playing());
}
}
});
if Button::new(text(locale, "controls-menu-volume")).ui(ui).clicked() {
dialogs.open_volume_controls();
ui.close_menu();
}
});
self.controls_menu(locale, ui, dialogs, &mut player);
menu::menu_button(ui, text(locale, "bookmarks-menu"), |ui| {
if Button::new(text(locale, "bookmarks-menu-add")).ui(ui).clicked() {
ui.close_menu();
Expand Down Expand Up @@ -417,6 +410,55 @@ impl MenuBar {
});
}

fn controls_menu(
&mut self,
locale: &LanguageIdentifier,
ui: &mut egui::Ui,
dialogs: &mut Dialogs,
player: &mut Option<&mut Player>,
) {
menu::menu_button(ui, text(locale, "controls-menu"), |ui| {
ui.add_enabled_ui(player.is_some(), |ui| {
let playing = player.as_ref().map(|p| p.is_playing()).unwrap_or_default();
let btn_name = if playing {
"controls-menu-suspend"
} else {
"controls-menu-resume"
};
if Button::new(text(locale, btn_name))
.shortcut_text(ui.ctx().format_shortcut(&Self::SHORTCUT_PAUSE))
.ui(ui)
.clicked()
{
ui.close_menu();
if let Some(player) = player {
player.set_is_playing(!playing);
}
}

ui.add_enabled_ui(!playing, |ui| {
if Button::new(text(locale, "controls-menu-step-once"))
.shortcut_text(ui.ctx().format_shortcut(&Self::SHORTCUT_STEP))
.ui(ui)
.clicked()
{
ui.close_menu();
if let Some(player) = player {
player.suspend_after_next_frame();
}
}
});
});
if Button::new(text(locale, "controls-menu-volume"))
.ui(ui)
.clicked()
{
dialogs.open_volume_controls();
ui.close_menu();
}
});
}

fn open_file(&mut self) {
let _ = self
.event_loop
Expand Down