mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-25 18:47:42 -06:00
@@ -75,6 +75,8 @@ mod tests_dialog_fit;
|
||||
#[cfg(test)]
|
||||
mod tests_dialogs;
|
||||
#[cfg(test)]
|
||||
mod tests_edit_layout;
|
||||
#[cfg(test)]
|
||||
mod tests_error_kinds;
|
||||
#[cfg(test)]
|
||||
mod tests_filmstrip;
|
||||
|
||||
@@ -165,7 +165,10 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(egui::RichText::new(crate::i18n::tr("Profile")).font(t.font(13.0)).color(t.text_dim));
|
||||
let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color");
|
||||
let r = crate::widgets::dropdown(ui, "profile", crate::i18n::profile_label(&d.profile.id, name), t.font(15.0), t.text_label);
|
||||
// a long name is cut short before Browse Profiles: a wider row would widen the whole panel's
|
||||
// content, and the sliders below would run past its edge
|
||||
let max = ui.available_width() - 28.0 - ui.spacing().item_spacing.x;
|
||||
let r = crate::widgets::dropdown_within(ui, "profile", crate::i18n::profile_label(&d.profile.id, name), t.font(15.0), t.text_label, max);
|
||||
egui::Popup::menu(&r).show(|ui| profile_menu(app, ui, &d));
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if crate::widgets::icon_button(
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
//! The Edit panel at its narrowest with a long profile applied: the profile row fits (#1759).
|
||||
|
||||
use egui::{Rect, vec2};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{LightcraftApp, Services, headless::HeadlessView};
|
||||
|
||||
const LONG_ID: &str = "lut:kodak-portra-400-plus-one-stop-overexposed";
|
||||
const LONG_NAME: &str = "Kodak Portra 400 Plus One Stop Overexposed";
|
||||
|
||||
fn text_shapes(shape: &egui::Shape, out: &mut Vec<egui::epaint::TextShape>) {
|
||||
match shape {
|
||||
egui::Shape::Text(text) => out.push(text.clone()),
|
||||
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| text_shapes(s, out)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn panel(width: f32) -> (Vec<egui::epaint::TextShape>, Vec<(String, Rect)>) {
|
||||
let mut app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services::default());
|
||||
app.session.lut_profiles.push(lightcraft_engine::cmd::lut_profiles::LutProfile {
|
||||
id: LONG_ID.into(),
|
||||
name: LONG_NAME.into(),
|
||||
group: "Film Stocks".into(),
|
||||
file: String::new(),
|
||||
});
|
||||
app.run("develop.profile", json!({"id": LONG_ID})).unwrap();
|
||||
let photo = app.session.active().unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
crate::theme::apply(&ctx);
|
||||
let mut texts = Vec::new();
|
||||
let mut widgets = Vec::new();
|
||||
for frame in 0..3 {
|
||||
let raw = HeadlessView::raw_input(vec2(width, 4000.0), 1.0, frame as f64 / 60.0, vec![]);
|
||||
let mut out = ctx.run_ui(raw, |ui| {
|
||||
egui::CentralPanel::default().frame(egui::Frame::NONE).show(ui, |ui| {
|
||||
crate::panels::edit::show(&mut app, ui, photo);
|
||||
});
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
texts.clear();
|
||||
for shape in &out.shapes {
|
||||
text_shapes(&shape.shape, &mut texts);
|
||||
}
|
||||
widgets = crate::widgets::take_registry(&ctx);
|
||||
}
|
||||
(texts, widgets)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_long_profile_name_leaves_the_edit_panel_its_width() {
|
||||
let width = crate::state::RIGHT_WIDTH.min;
|
||||
let (texts, widgets) = panel(width);
|
||||
let rect = |id: &str| widgets.iter().find(|(w, _)| w == id).unwrap_or_else(|| panic!("{id} not drawn")).1;
|
||||
let dropdown = rect("dropdown:profile");
|
||||
let browse = rect("icon:profileBrowser");
|
||||
assert!(dropdown.right() <= browse.left(), "the profile menu {dropdown:?} runs into Browse Profiles {browse:?}");
|
||||
let name = texts.iter().find(|t| t.galley.job.text == LONG_NAME).unwrap();
|
||||
assert!(name.galley.elided, "a name wider than the row is cut short");
|
||||
assert!(name.visual_bounding_rect().right() <= dropdown.right(), "the name leaves its menu");
|
||||
// every slider drawn, from the profile's Amount down
|
||||
rect("sliderValue:profile.amount");
|
||||
rect("sliderValue:light.exposure");
|
||||
for (id, r) in widgets.iter().filter(|(id, _)| id.starts_with("slider")) {
|
||||
assert!(r.right() <= width, "{id} {r:?} is cut off by the {width} pt panel");
|
||||
}
|
||||
}
|
||||
@@ -691,8 +691,15 @@ fn star_lit(i: u8, rating: u8, hover: Option<u8>) -> bool {
|
||||
|
||||
/// A borderless dropdown button: `text` followed by a painted chevron (no font glyph needed).
|
||||
pub fn dropdown(ui: &mut Ui, id: &str, text: &str, font: egui::FontId, color: Color32) -> Response {
|
||||
dropdown_within(ui, id, text, font, color, f32::INFINITY)
|
||||
}
|
||||
|
||||
/// [`dropdown`] at most `max` wide, chevron included: a longer `text` is cut short with …, and
|
||||
/// shown in full on hover.
|
||||
pub fn dropdown_within(ui: &mut Ui, id: &str, text: &str, font: egui::FontId, color: Color32, max: f32) -> Response {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let galley = ui.painter().layout_no_wrap(text.to_string(), font, color);
|
||||
let galley = one_line(ui.painter(), text, font, color, max - 20.0);
|
||||
let elided = galley.elided;
|
||||
let size = vec2(galley.size().x + 20.0, galley.size().y.max(20.0));
|
||||
let (r, resp) = ui.allocate_exact_size(size, Sense::click());
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::ComboBox, true, text));
|
||||
@@ -700,7 +707,7 @@ pub fn dropdown(ui: &mut Ui, id: &str, text: &str, font: egui::FontId, color: Co
|
||||
let c = if resp.hovered() { t.text } else { color };
|
||||
ui.painter().galley(pos2(r.left(), r.center().y - galley.size().y / 2.0), galley, c);
|
||||
paint(ui.painter(), Rect::from_center_size(pos2(r.right() - 10.0, r.center().y), vec2(16.0, 16.0)), Icon::ChevronDown, c);
|
||||
resp
|
||||
if elided { resp.on_hover_text(text) } else { resp }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user