mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 02:36:48 -06:00
Merge pull request #1877 from SocraticsAI:host-chrome
This commit is contained in:
@@ -209,9 +209,31 @@ pub struct Perf {
|
||||
last: f64,
|
||||
}
|
||||
|
||||
/// Which parts of the app's own window chrome are drawn. Everything is on by default; a host
|
||||
/// that embeds the sheet in its own window (and owns files itself) can turn off the title bar
|
||||
/// and the File tab, whose Backstage view then never opens. Not persisted: it describes the
|
||||
/// host, not a user preference.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Chrome {
|
||||
/// The title bar: AutoSave, Home, Save, Undo/Redo, the file name, Comments, Share, Search and
|
||||
/// the theme switch.
|
||||
pub title_bar: bool,
|
||||
/// The ribbon's File tab and the Backstage view it opens (also reached by keyboard and
|
||||
/// commands such as `ui.backstage`).
|
||||
pub file_tab: bool,
|
||||
}
|
||||
|
||||
impl Default for Chrome {
|
||||
fn default() -> Self {
|
||||
Chrome { title_bar: true, file_tab: true }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SheetApp {
|
||||
pub session: Session,
|
||||
pub ui: UiState,
|
||||
/// The window chrome to draw (see [`Chrome`]).
|
||||
pub chrome: Chrome,
|
||||
pub views: HashMap<(u64, usize), SheetView>,
|
||||
pub editor: Option<editor::EditState>,
|
||||
pub text_box_editor: Option<text_box::EditState>,
|
||||
@@ -270,6 +292,7 @@ impl SheetApp {
|
||||
SheetApp {
|
||||
session,
|
||||
ui: UiState::default(),
|
||||
chrome: Chrome::default(),
|
||||
views: HashMap::new(),
|
||||
editor: None,
|
||||
text_box_editor: None,
|
||||
@@ -752,6 +775,9 @@ impl SheetApp {
|
||||
/// Opens the File tab's Backstage view on `page`, committing a cell still being edited first
|
||||
/// (File actions work on the whole workbook). A draft that can't be committed keeps the sheet.
|
||||
pub fn open_backstage(&mut self, page: backstage::Page) {
|
||||
if !self.chrome.file_tab {
|
||||
return;
|
||||
}
|
||||
if !self.commit_edit(0, 0, false, false) {
|
||||
return;
|
||||
}
|
||||
@@ -1115,10 +1141,16 @@ impl SheetApp {
|
||||
// The language the widgets translate with this frame (see `i18n::current`).
|
||||
i18n::set_current(&ctx, self.ui.language);
|
||||
let t = theme::Tokens::get(&ctx);
|
||||
// A host can turn the File tab off while Backstage is already open: close it.
|
||||
if !self.chrome.file_tab {
|
||||
self.backstage = None;
|
||||
}
|
||||
if self.backstage.is_none() {
|
||||
self.ribbon_keys(&ctx);
|
||||
}
|
||||
ribbon::title_bar(self, ui);
|
||||
if self.chrome.title_bar {
|
||||
ribbon::title_bar(self, ui);
|
||||
}
|
||||
if self.backstage.is_some() {
|
||||
// File tab: the Backstage view replaces everything below the title bar.
|
||||
backstage::show(self, ui);
|
||||
|
||||
@@ -176,7 +176,9 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 2.0;
|
||||
ui.add_space(8.0);
|
||||
file_menu(app, ui);
|
||||
if app.chrome.file_tab {
|
||||
file_menu(app, ui);
|
||||
}
|
||||
let mut tabs: Vec<&str> = TABS.to_vec();
|
||||
let ctx_tabs = contextual_tabs(app);
|
||||
tabs.extend(ctx_tabs.iter());
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//! A host that embeds the sheet in its own window can turn off the app's title bar and File tab.
|
||||
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
use gridcraft_engine::Session;
|
||||
use gridcraft_ui_egui::{Chrome, Services, SheetApp, backstage::Page};
|
||||
|
||||
fn harness(chrome: Chrome) -> egui_kittest::Harness<'static, SheetApp> {
|
||||
let mut session = Session::new();
|
||||
session.new_workbook();
|
||||
let mut app = SheetApp::new(session, Services::default());
|
||||
app.chrome = chrome;
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_ui_state(
|
||||
|ui, app: &mut SheetApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(4);
|
||||
h
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_title_bar_and_file_tab_are_drawn_by_default() {
|
||||
let h = harness(Chrome::default());
|
||||
assert!(h.query_by_label("AutoSave").is_some(), "the title bar is drawn");
|
||||
assert!(h.query_by_label("File").is_some(), "the File tab is drawn");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_host_can_leave_out_the_title_bar_and_the_file_tab() {
|
||||
let full = harness(Chrome::default());
|
||||
let h = harness(Chrome { title_bar: false, file_tab: false });
|
||||
assert!(h.query_by_label("AutoSave").is_none(), "no title bar");
|
||||
assert!(h.query_by_label("File").is_none(), "no File tab");
|
||||
// The ribbon tabs are painted text, so measure the move with the first ribbon control.
|
||||
let (with_bar, without) = (full.get_by_label("Paste").rect(), h.get_by_label("Paste").rect());
|
||||
let moved = with_bar.min.y - without.min.y;
|
||||
assert!((25.0..=40.0).contains(&moved), "the ribbon moves up by the title bar's height (28 on macOS, 38 elsewhere), moved {moved}");
|
||||
assert_eq!(with_bar.min.x, without.min.x, "the ribbon's groups keep their place across");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_the_file_tab_the_backstage_view_never_opens() {
|
||||
let mut h = harness(Chrome { title_bar: true, file_tab: false });
|
||||
h.state_mut().open_backstage(Page::Home);
|
||||
h.run_steps(3);
|
||||
assert!(h.state().backstage.is_none(), "opening Backstage directly is ignored");
|
||||
h.state_mut().open_backstage(Page::Print);
|
||||
h.run_steps(3);
|
||||
assert!(h.state().backstage.is_none(), "so is every page, Print and Save As included");
|
||||
assert!(h.query_by_label("AutoSave").is_some(), "the title bar can stay on its own");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turning_the_file_tab_off_closes_an_open_backstage() {
|
||||
let mut h = harness(Chrome::default());
|
||||
h.state_mut().open_backstage(Page::Home);
|
||||
h.run_steps(3);
|
||||
assert!(h.state().backstage.is_some(), "Backstage opens with the File tab on");
|
||||
h.state_mut().chrome.file_tab = false;
|
||||
h.run_steps(3);
|
||||
assert!(h.state().backstage.is_none(), "and closes when the host turns the File tab off");
|
||||
}
|
||||
Reference in New Issue
Block a user