mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Merge PR #542 from NikitkaRa: Design: Variants gallery with four colour variations of the theme
# Conflicts: # crates/engine/src/cmd/design.rs # crates/engine/src/tests.rs
This commit is contained in:
@@ -34,6 +34,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_doc,
|
||||
colors
|
||||
),
|
||||
cmd!("design.variant", "Variants", ["Design", "Variants"], None, "{index: 0..3} (see design.variants)", has_doc, variant),
|
||||
cmd!(query "design.variants", "Variant Gallery", [], None, "{} → the current theme's four colour variants and which is applied", has_doc, variants),
|
||||
cmd!("design.fonts", "Fonts", ["Design", "Variants"], None, "{} opens Create New Theme Fonts | {name} | {major, minor}", has_doc, fonts),
|
||||
cmd!("design.effects", "Effects", ["Design", "Variants"], None, "{name} (see design.themes)", has_doc, effects),
|
||||
cmd!(
|
||||
@@ -257,6 +259,32 @@ fn colors(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
ok()
|
||||
}
|
||||
|
||||
fn variant(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let index = p.get("index").and_then(Value::as_u64).ok_or_else(|| bad("design.variant", "missing `index` (0..3)"))? as usize;
|
||||
s.edit(|doc, sel| {
|
||||
let mi = master_index(doc, sel);
|
||||
let m = Arc::make_mut(doc.masters.get_mut(mi).ok_or_else(|| bad("design.variant", "no master"))?);
|
||||
let v = deckcraft_model::theme::theme_variants(&m.theme);
|
||||
m.theme.colors =
|
||||
v.get(index).cloned().ok_or_else(|| bad("design.variant", format!("no variant {index}; 0..{}", v.len().saturating_sub(1))))?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(json!({"variant": index}))
|
||||
}
|
||||
|
||||
fn variants(s: &mut Session, _p: &Value) -> Result<Value> {
|
||||
let st = s.doc()?;
|
||||
let mi = master_index(&st.doc, &st.selection);
|
||||
let th = st.doc.masters.get(mi).map(|m| m.theme.clone()).unwrap_or_default();
|
||||
let v = deckcraft_model::theme::theme_variants(&th);
|
||||
let current = v.iter().position(|c| c.colors == th.colors.colors);
|
||||
Ok(json!({
|
||||
"theme": th.name,
|
||||
"current": current,
|
||||
"variants": v.iter().map(|c| json!({"name": c.name, "colors": c.colors.iter().map(|x| format!("#{}", x.hex())).collect::<Vec<_>>()})).collect::<Vec<_>>(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn current_colors(s: &Session) -> Result<deckcraft_color::ColorScheme> {
|
||||
let st = s.doc()?;
|
||||
let mi = master_index(&st.doc, &st.selection);
|
||||
|
||||
@@ -2314,3 +2314,31 @@ fn autocorrect_options_as_you_type() {
|
||||
s.execute("text.insert", &json!({"text": "monday notes "})).unwrap();
|
||||
assert_eq!(s.execute("text.get", &json!({"id": title})).unwrap()["text"], "monday notes ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn theme_variants_apply_and_report_the_current_one() {
|
||||
let mut s = session();
|
||||
let name = deckcraft_model::theme::builtin_themes()[1].name.clone();
|
||||
s.execute("design.theme", &json!({"name": name})).unwrap();
|
||||
let v = s.execute("design.variants", &json!({})).unwrap();
|
||||
assert_eq!(v["variants"].as_array().unwrap().len(), 4);
|
||||
assert_eq!(v["current"], json!(0));
|
||||
let base = s.doc().unwrap().doc.masters[0].theme.colors.clone();
|
||||
s.execute("design.variant", &json!({"index": 1})).unwrap();
|
||||
let c = s.doc().unwrap().doc.masters[0].theme.colors.clone();
|
||||
assert_eq!(c.colors[4], base.colors[5], "accents rotate");
|
||||
assert_eq!(c.colors[0], base.colors[0]);
|
||||
assert_eq!(s.execute("design.variants", &json!({})).unwrap()["current"], json!(1));
|
||||
// From a variant, another variant is built from the theme's own colours, not the variant's.
|
||||
s.execute("design.variant", &json!({"index": 3})).unwrap();
|
||||
let d = s.doc().unwrap().doc.masters[0].theme.colors.clone();
|
||||
assert_eq!((d.colors[0], d.colors[1], d.colors[4]), (base.colors[1], base.colors[0], base.colors[4]));
|
||||
s.execute("design.variant", &json!({"index": 0})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc.masters[0].theme.colors.colors, base.colors);
|
||||
assert!(s.execute("design.variant", &json!({"index": 9})).is_err());
|
||||
// It survives PPTX.
|
||||
s.execute("design.variant", &json!({"index": 2})).unwrap();
|
||||
let p = s.doc().unwrap().doc.as_ref().clone();
|
||||
let back = deckcraft_pptx::import(&deckcraft_pptx::export(&p).unwrap()).unwrap();
|
||||
assert_eq!(back.masters[0].theme.colors.colors, p.masters[0].theme.colors.colors);
|
||||
}
|
||||
|
||||
@@ -149,6 +149,31 @@ pub fn default_format() -> FormatScheme {
|
||||
}
|
||||
}
|
||||
|
||||
/// Design ▸ Variants: four colour variations of `t`, built from its built-in colours when it is
|
||||
/// a built-in theme (so picking one variant after another doesn't drift), else from its own.
|
||||
/// 1: as designed; 2 and 3: the accents rotated; 4: dark (light and dark colours swapped).
|
||||
pub fn theme_variants(t: &Theme) -> Vec<ColorScheme> {
|
||||
let base = builtin_themes().into_iter().find(|b| b.name == t.name).map_or_else(|| t.colors.clone(), |b| b.colors);
|
||||
let rotate = |by: usize| {
|
||||
let mut c = base.clone();
|
||||
let accents: Vec<_> = base.colors.get(4..10).map(<[_]>::to_vec).unwrap_or_default();
|
||||
for (i, slot) in c.colors.iter_mut().skip(4).take(6).enumerate() {
|
||||
if let Some(a) = accents.get((i + by) % accents.len().max(1)) {
|
||||
*slot = *a;
|
||||
}
|
||||
}
|
||||
c
|
||||
};
|
||||
let mut dark = base.clone();
|
||||
dark.colors.swap(0, 1);
|
||||
dark.colors.swap(2, 3);
|
||||
let mut out = vec![base.clone(), rotate(1), rotate(3), dark];
|
||||
for (i, c) in out.iter_mut().enumerate().skip(1) {
|
||||
c.name = format!("{} {}", base.name, i + 1);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Our original built-in themes (shown in Design ▸ Themes). Colours and names are our own.
|
||||
pub fn builtin_themes() -> Vec<Theme> {
|
||||
let t = |name: &str, colors: ColorScheme, major: &str, minor: &str| Theme {
|
||||
|
||||
@@ -1452,6 +1452,29 @@ fn design(app: &mut SlideApp, ui: &mut Ui) {
|
||||
run(app, "app.browseThemeDialog", json!({}));
|
||||
}
|
||||
});
|
||||
// Variants: the current theme in its four colour variations.
|
||||
if let Some(th) = app.session.active().and_then(|d| d.doc.masters.first().map(|m| m.theme.clone())) {
|
||||
let variants = deckcraft_model::theme::theme_variants(&th);
|
||||
let tiles: Vec<deckcraft_model::Theme> = variants.iter().map(|c| deckcraft_model::Theme { colors: c.clone(), ..th.clone() }).collect();
|
||||
let names: Vec<String> = variants.iter().map(|c| c.name.clone()).collect();
|
||||
let sel = variants.iter().position(|c| c.colors == th.colors.colors);
|
||||
group(ui, |ui| {
|
||||
if let Some(i) = widgets::gallery(
|
||||
ui,
|
||||
tiles.len(),
|
||||
vec2(84.0, 56.0),
|
||||
sel,
|
||||
|p, r, i| {
|
||||
if let Some(t) = tiles.get(i) {
|
||||
paint_theme_tile(p, r, t);
|
||||
}
|
||||
},
|
||||
|i| names.get(i).cloned().unwrap_or_default(),
|
||||
) {
|
||||
run(app, "design.variant", json!({"index": i}));
|
||||
}
|
||||
});
|
||||
}
|
||||
group(ui, |ui| {
|
||||
let r = big_button(ui, Icon::Colors, "Colors", true);
|
||||
r.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, "Colors"));
|
||||
|
||||
@@ -26,6 +26,8 @@ The summary between `<!-- SUMMARY -->
|
||||
| Masters & themes | 85% |
|
||||
| Views | 90% |
|
||||
| Masters & themes | 88% |
|
||||
| Views | 90% |
|
||||
| Masters & themes | 88% |
|
||||
| Shapes | 88% |
|
||||
| Shapes | 91% |
|
||||
| Format | 83% |
|
||||
@@ -105,7 +107,7 @@ Open P0 items: Contextual tabs (Shape Format, Picture Format, Table Design/Layou
|
||||
| Masters & themes | Insert/rename/delete layouts, insert placeholders | P1 | D |
|
||||
| Masters & themes | Handout and Notes masters | P2 | M |
|
||||
| Masters & themes | Built-in themes (original designs) | P0 | D |
|
||||
| Masters & themes | Theme variants | P1 | P |
|
||||
| Masters & themes | Theme variants | P1 | D |
|
||||
| Masters & themes | Colour schemes (built-in and custom) | P0 | D |
|
||||
| Masters & themes | Font schemes (built-in and custom) | P0 | D |
|
||||
| Masters & themes | Effect schemes / background styles | P2 | P |
|
||||
|
||||
Reference in New Issue
Block a user