mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-14 19:26:59 -04:00
Merge pull request #1217 from liangforstudy/fix-movable-dialogs
dialogs: drag them by the title bar (open centred, no longer anchored)
This commit is contained in:
commit
1fdfd8324d
19 files changed
+114
-36
No files matched your search
@@ -1909,7 +1909,8 @@ impl FilmcraftApp {
|
||||
.id(egui::Id::new("FilmCraft hit an error"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.set_max_width(460.0);
|
||||
ui.label(tl!("Something went wrong while drawing the window. Your project is still open; save it to be safe."));
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
//! - Quit (asked when the window is closed with unsaved changes, see [`intercept_quit`])
|
||||
//! `quit.save`, `quit.dontSave`, `quit.cancel`.
|
||||
|
||||
use egui::{Align2, RichText};
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use egui::RichText;
|
||||
use filmcraft_project::{AudioChannelMap, AudioChannels, ItemKind, TrackKind};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
@@ -311,7 +312,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let can_save = app.session.path.is_some() || app.hooks.pick_save.is_some();
|
||||
// the title is translated; the window keeps one id whatever the interface language
|
||||
let id = egui::Id::new(("clip-dialog", pre));
|
||||
egui::Window::new(title).id(id).collapsible(false).resizable(false).default_width(360.0).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
egui::Window::new(title).id(id).collapsible(false).resizable(false).default_width(360.0).centered(ctx).show(ctx, |ui| {
|
||||
let p = &mut d.params;
|
||||
match d.command.as_str() {
|
||||
"edit.pasteAttributes" | "edit.removeAttributes" => {
|
||||
|
||||
@@ -37,7 +37,8 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.open(&mut open)
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| about(app, ui));
|
||||
if crate::widgets::escape_closes(ctx) {
|
||||
open = false;
|
||||
@@ -112,7 +113,8 @@ fn audio_gain(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.id(egui::Id::new("Audio Gain"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.add_space(6.0);
|
||||
egui::Grid::new("audio-gain").num_columns(2).spacing([12.0, 10.0]).show(ui, |ui| {
|
||||
@@ -200,7 +202,8 @@ fn add_tracks(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(320.0)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
// everything spans the width the window has: no empty band beside the fields
|
||||
ui.set_width(ui.available_width());
|
||||
@@ -329,7 +332,8 @@ fn delete_tracks(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.id(egui::Id::new("Delete Tracks"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.set_min_width(300.0);
|
||||
for (kind, title, delete, on, target, list) in [
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
//! `program.layer.<clip>.<layer>.corner.<n>`,
|
||||
//! `program.textEdit`, `graphics.*` in the panels.
|
||||
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use egui::{Align2, Color32, Pos2, Rect, Sense, Stroke, StrokeKind, pos2, vec2};
|
||||
use filmcraft_geom::{Affine, Vec2};
|
||||
use filmcraft_project::graphic::{self, LayerContent, LayerSpec, eval_layer, layer_display_name, layer_indices};
|
||||
@@ -1823,7 +1824,7 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let mut ok = false;
|
||||
let name = |paragraph: bool| if paragraph { tl!("Paragraph Text") } else { tl!("Point Text") };
|
||||
let id = egui::Id::new("gfx-text-properties");
|
||||
egui::Window::new(tl!("Text Properties")).id(id).collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
egui::Window::new(tl!("Text Properties")).id(id).collapsible(false).resizable(false).centered(ctx).show(ctx, |ui| {
|
||||
ui.label(tl!("Text Layer Type"));
|
||||
let r = egui::ComboBox::from_id_salt("gfx-text-type").selected_text(name(d.paragraph)).width(190.0).show_ui(ui, |ui| {
|
||||
for paragraph in [false, true] {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//! dialogs: `exportTemplate.name|category|control.<n>|ok|cancel`, `replaceFonts.from|to|ok|cancel`.
|
||||
//! The UI state (`UiState::gfx_templates`) is serde, so agents can read and set it.
|
||||
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::{Align2, Color32, Rect, Sense, Stroke, StrokeKind, pos2, vec2};
|
||||
@@ -650,7 +651,7 @@ fn export_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(420.0)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.centered(ctx)
|
||||
.show(ctx, |ui| {
|
||||
egui::Grid::new("gfx-export-grid").num_columns(2).show(ui, |ui| {
|
||||
ui.label(tl!("Name:"));
|
||||
@@ -729,7 +730,7 @@ fn replace_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(440.0)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.centered(ctx)
|
||||
.show(ctx, |ui| {
|
||||
ui.label(tl!("Fonts used in the project:"));
|
||||
egui::Grid::new("gfx-fonts-used").striped(true).num_columns(3).show(ui, |ui| {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! `<p>.audioFormat.<wav|aiff|mxf>` (mxf: AAF only), `<p>.sampleRate.<rate>`, `<p>.bitDepth.<16|24>`, `<p>.trimAudio`,
|
||||
//! `<p>.handles` (frames), `<p>.renderAudioEffects`, `<p>.title` (OMF), `<p>.ok`, `<p>.cancel`.
|
||||
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::FilmcraftApp;
|
||||
@@ -70,7 +71,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let mut elems: Vec<(String, egui::Rect, String)> = Vec::new();
|
||||
let title = if d.omf { "OMF Export Settings" } else { "AAF Export Settings" };
|
||||
let shown = if d.omf { tl!("OMF Export Settings") } else { tl!("AAF Export Settings") };
|
||||
egui::Window::new(shown).id(egui::Id::new(title)).collapsible(false).resizable(false).anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
egui::Window::new(shown).id(egui::Id::new(title)).collapsible(false).resizable(false).centered(ctx).show(ctx, |ui| {
|
||||
ui.set_min_width(380.0);
|
||||
egui::Grid::new("interchange-export-grid").num_columns(2).spacing([12.0, 8.0]).show(ui, |ui| {
|
||||
if d.omf {
|
||||
|
||||
@@ -806,7 +806,8 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.id(egui::Id::new("mb-edit-columns"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
let mut mv: Option<(usize, isize)> = None;
|
||||
for c in mb::ALL_COLUMNS {
|
||||
|
||||
@@ -166,7 +166,8 @@ fn link_media(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.resizable(false)
|
||||
.default_width(width)
|
||||
.max_width(width)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
egui::ScrollArea::vertical().id_salt("link-media-body-scroll").max_height((ctx.content_rect().height() - 140.0).max(1.0)).show(ui, |ui| {
|
||||
ui.label(tlf!("{n} clip(s) can't find their media. Locate them, search a folder, or leave them offline.", n = rows.len()));
|
||||
@@ -373,7 +374,8 @@ fn make_offline(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.id(egui::Id::new("Make Offline"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.label(tlf!("Make {n} selected clip(s) offline. Their clips show the offline slate until you link them again.", n));
|
||||
let r = ui.radio(!delete, tl!("Media files remain on disk"));
|
||||
@@ -417,7 +419,8 @@ fn create_proxies(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.id(egui::Id::new("Create Proxies"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.label(tlf!("{n} clip(s) selected.", n = d.items.len()));
|
||||
ui.add_space(4.0);
|
||||
@@ -496,7 +499,8 @@ fn project_manager(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.id(egui::Id::new("Project Manager"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.label(RichText::new(tl!("Sequences")).strong());
|
||||
for (id, name) in &seqs {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
//! `find.row.<n>.column|operator|text`; Project Settings tabs `projectSettings.tab.<general|scratchDisks|ingest>`,
|
||||
//! scratch rows `projectSettings.scratch.<key>` / `.browse` / `.same`.
|
||||
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use egui::{Align2, RichText};
|
||||
use filmcraft_project::{FindOp, find::COLUMNS};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -463,7 +464,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let info_only = matches!(d.command.as_str(), "file.mediaProperties" | "clip.sourceSettings" | "help.systemCompatibilityReport");
|
||||
// the title is translated; the window keeps one id whatever the interface language
|
||||
let id = egui::Id::new(("menu-dialog", pre));
|
||||
egui::Window::new(title).id(id).collapsible(false).resizable(false).default_width(420.0).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
egui::Window::new(title).id(id).collapsible(false).resizable(false).default_width(420.0).centered(ctx).show(ctx, |ui| {
|
||||
let p = &mut d.params;
|
||||
match d.command.as_str() {
|
||||
"edit.find" => {
|
||||
|
||||
@@ -803,9 +803,13 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
GuideDialog::SaveTemplate { .. } => ("Save Guides as Template", tl!("Save Guides as Template")),
|
||||
GuideDialog::Manage { .. } => ("Manage Guides", tl!("Manage Guides")),
|
||||
};
|
||||
egui::Window::new(shown).id(egui::Id::new(title)).collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(
|
||||
ctx,
|
||||
|ui| match &mut d {
|
||||
egui::Window::new(shown)
|
||||
.id(egui::Id::new(title))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| match &mut d {
|
||||
GuideDialog::Add { vertical, position, source } => {
|
||||
let m = if *source { "source" } else { "program" };
|
||||
ui.horizontal(|ui| {
|
||||
@@ -893,8 +897,7 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
close |= r.clicked();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
for (id, r, l) in elems {
|
||||
app.auto.add(&id, r, &l);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ pub fn show_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(440.0)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
if pre != "sync" {
|
||||
ui.horizontal(|ui| {
|
||||
@@ -456,7 +457,8 @@ pub fn show_edit_cameras(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(420.0)
|
||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.label(RichText::new(tl!("Cameras shown in the Multi-Camera view (uncheck to hide).")).weak().small());
|
||||
egui::ScrollArea::vertical().max_height(420.0).show(ui, |ui| {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! and Save Arrangement…. Drafts live in `UiState::project_panel.dialog` (serde, so agents fill
|
||||
//! them with `ui.set`); OK runs one engine command.
|
||||
|
||||
use egui::Align2;
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::FilmcraftApp;
|
||||
@@ -25,7 +25,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
ProjectDialog::FreeformOptions { .. } => tl!("Freeform View Options"),
|
||||
ProjectDialog::SaveArrangement { .. } => tl!("Save Arrangement"),
|
||||
};
|
||||
let mut win = egui::Window::new(title).id(egui::Id::new("project-dialog")).collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]);
|
||||
let mut win = egui::Window::new(title).id(egui::Id::new("project-dialog")).collapsible(false).resizable(false).centered(ctx);
|
||||
// a fixed size keeps the two-list dialog from shifting while its lists settle
|
||||
if matches!(d, ProjectDialog::MetadataDisplay { .. }) {
|
||||
win = win.fixed_size(egui::vec2(480.0, 470.0));
|
||||
|
||||
@@ -52,7 +52,8 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
.id(egui::Id::new("Remix Properties"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.set_min_width(320.0);
|
||||
egui::Grid::new("remix-props").num_columns(2).spacing([12.0, 10.0]).show(ui, |ui| {
|
||||
|
||||
@@ -491,7 +491,8 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(620.0)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.set_width(620.0);
|
||||
ui.horizontal(|ui| {
|
||||
|
||||
@@ -156,7 +156,8 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.fixed_size(size)
|
||||
.anchor(Align2::CENTER_CENTER, vec2(0.0, 0.0))
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.frame(egui::Frame::window(&ctx.global_style()).fill(t.panel_bg).inner_margin(14.0))
|
||||
.show(ctx, |ui| {
|
||||
ui.set_width(size.x);
|
||||
|
||||
@@ -507,7 +507,8 @@ pub fn duration_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.default_width(260.0)
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tl!("Duration:"));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//! `voiceover.ok`, `voiceover.cancel`; the countdown overlay is `voiceover.countdown.overlay`.
|
||||
//! UI commands: `voiceover.recordToggle {track?}` and `voiceover.settingsDialog`.
|
||||
|
||||
use crate::widgets::CenteredDialog as _;
|
||||
use egui::{Align2, Color32, Rect, RichText, Sense, Stroke, pos2, vec2};
|
||||
use filmcraft_engine::voiceover::VoiceOverPrefs;
|
||||
use filmcraft_project::TrackId;
|
||||
@@ -239,7 +240,7 @@ fn settings_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let accent = app.tokens.accent;
|
||||
// the title is translated; the window keeps one id whatever the interface language
|
||||
let id = egui::Id::new("voiceover-settings");
|
||||
egui::Window::new(tl!("Voice-Over Record Settings")).id(id).collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(ctx, |ui| {
|
||||
egui::Window::new(tl!("Voice-Over Record Settings")).id(id).collapsible(false).resizable(false).centered(ctx).show(ctx, |ui| {
|
||||
egui::Grid::new("voiceover-grid").num_columns(2).spacing([12.0, 8.0]).show(ui, |ui| {
|
||||
ui.label(tl!("Name:"));
|
||||
let r = ui.add(egui::TextEdit::singleline(&mut d.prefs.name).desired_width(220.0));
|
||||
|
||||
@@ -180,9 +180,13 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
WorkspaceDialog::SaveAs { .. } => ("New Workspace", tl!("New Workspace")),
|
||||
WorkspaceDialog::Edit { .. } => ("Edit Workspaces", tl!("Edit Workspaces")),
|
||||
};
|
||||
egui::Window::new(shown).id(egui::Id::new(title)).collapsible(false).resizable(false).anchor(Align2::CENTER_CENTER, [0.0, 0.0]).show(
|
||||
ctx,
|
||||
|ui| match &mut d {
|
||||
egui::Window::new(shown)
|
||||
.id(egui::Id::new(title))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.pivot(Align2::CENTER_CENTER)
|
||||
.default_pos(crate::widgets::dialog_center(ctx))
|
||||
.show(ctx, |ui| match &mut d {
|
||||
WorkspaceDialog::SaveAs { name } => {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tl!("Name:"));
|
||||
@@ -245,8 +249,7 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
close |= r.clicked();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
for (id, r, l) in elems {
|
||||
app.auto.add(&id, r, &l);
|
||||
}
|
||||
|
||||
@@ -294,6 +294,24 @@ pub fn dropdown_text(ui: &mut Ui, rect: Rect, text: &str, t: &Tokens, id: egui::
|
||||
resp
|
||||
}
|
||||
|
||||
/// Where dialogs open: the centre of the app window. Dialogs use it as `default_pos` with a
|
||||
/// centre pivot instead of `Window::anchor`, which would pin them: egui makes anchored windows
|
||||
/// immovable, so they could not be dragged by the title bar.
|
||||
pub fn dialog_center(ctx: &egui::Context) -> egui::Pos2 {
|
||||
ctx.content_rect().center()
|
||||
}
|
||||
|
||||
/// `.pivot(CENTER_CENTER).default_pos(dialog_center(ctx))` in one call, for short window chains.
|
||||
pub trait CenteredDialog {
|
||||
fn centered(self, ctx: &egui::Context) -> Self;
|
||||
}
|
||||
|
||||
impl CenteredDialog for egui::Window<'_> {
|
||||
fn centered(self, ctx: &egui::Context) -> Self {
|
||||
self.pivot(Align2::CENTER_CENTER).default_pos(dialog_center(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
/// Level meter bar (dBFS), vertical.
|
||||
pub fn meter_bar(painter: &egui::Painter, rect: Rect, db: f32, peak_db: f32, t: &Tokens) {
|
||||
// Premiere: green gradient (#579f51 → #70dc5d), yellow above −12 dB, red above −3 dB.
|
||||
@@ -368,4 +386,36 @@ mod tests {
|
||||
let range = st.cursor.char_range().map(|c| c.sorted_cursors());
|
||||
assert_eq!(range, Some([egui::text::CCursor::new(0), egui::text::CCursor::new(5)]));
|
||||
}
|
||||
|
||||
/// Dialogs open centred and can be dragged by their title bar (anchored ones could not).
|
||||
#[test]
|
||||
fn dialogs_open_centered_and_drag() {
|
||||
let ctx = egui::Context::default();
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1200.0, 800.0));
|
||||
let frame = |events: Vec<egui::Event>| {
|
||||
let input = egui::RawInput { screen_rect: Some(screen), events, ..Default::default() };
|
||||
let mut rect = egui::Rect::NOTHING;
|
||||
let mut out = ctx.run_ui(input, |ui| {
|
||||
let ctx = ui.ctx().clone();
|
||||
let win = egui::Window::new("Dialog").collapsible(false).resizable(false);
|
||||
if let Some(r) = win.pivot(egui::Align2::CENTER_CENTER).default_pos(super::dialog_center(&ctx)).show(&ctx, |ui| ui.label("body")) {
|
||||
rect = r.response.rect;
|
||||
}
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
rect
|
||||
};
|
||||
frame(Vec::new());
|
||||
let start = frame(Vec::new());
|
||||
assert!((start.center() - screen.center()).length() < 1.0, "opens centred: {start:?}");
|
||||
let grab = egui::pos2(start.center().x, start.min.y + 8.0);
|
||||
let to = grab + egui::vec2(-200.0, -150.0);
|
||||
let press = |p, pressed| egui::Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(vec![egui::Event::PointerMoved(grab), press(grab, true)]);
|
||||
frame(vec![egui::Event::PointerMoved(grab + egui::vec2(-100.0, -75.0))]);
|
||||
frame(vec![egui::Event::PointerMoved(to)]);
|
||||
frame(vec![press(to, false)]);
|
||||
let end = frame(Vec::new());
|
||||
assert!((end.center() - (start.center() + egui::vec2(-200.0, -150.0))).length() < 2.0, "moved with the drag: {start:?} -> {end:?}");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user