Add demo artwork
This commit is contained in:
parent
0e97a256b7
commit
0dcfafbf64
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -79,7 +79,7 @@ pub const DEFAULT_FONT_FAMILY: &str = "Merriweather";
|
|||
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
||||
|
||||
// Document
|
||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.17"; // Remember to save a simple document and replace the test file `graphite-test-document.graphite`
|
||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.17"; // Remember to update the demo artwork in /demos with both this version number and the contents so it remains editable
|
||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
||||
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
||||
|
|
|
|||
|
|
@ -555,7 +555,8 @@ mod test {
|
|||
println!("-------------------------------------------------");
|
||||
println!("Failed test due to receiving a DisplayDialogError while loading the Graphite sample file.");
|
||||
println!("This is most likely caused by forgetting to bump the `GRAPHITE_DOCUMENT_VERSION` in `editor/src/consts.rs`");
|
||||
println!("After bumping this version number, replace `graphite-test-document.graphite` with a valid file by saving a document from the editor.");
|
||||
println!("After bumping this version number, update the documents in `/demo-artwork` by editing their JSON to");
|
||||
println!("ensure they remain compatible with both the bumped version number and the serialization format change.");
|
||||
println!("DisplayDialogError details:");
|
||||
println!();
|
||||
println!("Description: {}", value);
|
||||
|
|
@ -567,19 +568,25 @@ mod test {
|
|||
init_logger();
|
||||
let mut editor = Editor::create();
|
||||
|
||||
let test_file = include_str!("../graphite-test-document.graphite");
|
||||
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||
document_name: "Graphite Version Test".into(),
|
||||
document_serialized_content: test_file.into(),
|
||||
});
|
||||
let test_files = [
|
||||
("Just a Potted Cactus", include_str!("../../demo-artwork/just-a-potted-cactus.graphite")),
|
||||
("Valley of Spires", include_str!("../../demo-artwork/valley-of-spires.graphite")),
|
||||
];
|
||||
|
||||
for response in responses {
|
||||
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
||||
if let FrontendMessage::UpdateDialogDetails { layout_target: _, diff } = response {
|
||||
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
||||
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
||||
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
||||
print_problem_to_terminal_on_failure(value);
|
||||
for (document_name, document_serialized_content) in test_files {
|
||||
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||
document_name: document_name.into(),
|
||||
document_serialized_content: document_serialized_content.into(),
|
||||
});
|
||||
|
||||
for response in responses {
|
||||
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
||||
if let FrontendMessage::UpdateDialogDetails { layout_target: _, diff } = response {
|
||||
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
||||
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
||||
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
||||
print_problem_to_terminal_on_failure(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub enum DialogMessage {
|
|||
RequestComingSoonDialog {
|
||||
issue: Option<i32>,
|
||||
},
|
||||
RequestDemoArtworkDialog,
|
||||
RequestExportDialog,
|
||||
RequestNewDocumentDialog,
|
||||
RequestPreferencesDialog,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog};
|
||||
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct DialogMessageHandler {
|
||||
export_dialog: ExportDialogMessageHandler,
|
||||
|
|
@ -53,6 +54,11 @@ impl MessageHandler<DialogMessage, (&PortfolioMessageHandler, &PreferencesMessag
|
|||
coming_soon.send_layout(responses, LayoutTarget::DialogDetails);
|
||||
responses.add(FrontendMessage::DisplayDialog { icon: "Warning".to_string() });
|
||||
}
|
||||
DialogMessage::RequestDemoArtworkDialog => {
|
||||
let demo_artwork_dialog = DemoArtworkDialog;
|
||||
demo_artwork_dialog.send_layout(responses, LayoutTarget::DialogDetails);
|
||||
responses.add(FrontendMessage::DisplayDialog { icon: "Image".to_string() });
|
||||
}
|
||||
DialogMessage::RequestExportDialog => {
|
||||
if let Some(document) = portfolio.active_document() {
|
||||
let artboard_handler = &document.artboard_message_handler;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
|||
|
||||
impl LayoutHolder for NewDocumentDialogMessageHandler {
|
||||
fn layout(&self) -> Layout {
|
||||
let title = vec![TextLabel::new("New document").bold(true).widget_holder()];
|
||||
let title = vec![TextLabel::new("New Document").bold(true).widget_holder()];
|
||||
|
||||
let name = vec![
|
||||
TextLabel::new("Name").table_align(true).widget_holder(),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ impl LayoutHolder for CloseAllDocumentsDialog {
|
|||
fn layout(&self) -> Layout {
|
||||
let discard = TextButton::new("Discard All")
|
||||
.min_width(96)
|
||||
.emphasized(true)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![PortfolioMessage::CloseAllDocuments.into()],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
/// A dialog to let the user browse a gallery of demo artwork that can be opened.
|
||||
pub struct DemoArtworkDialog;
|
||||
|
||||
impl LayoutHolder for DemoArtworkDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
let artwork = [
|
||||
(
|
||||
"Valley of Spires",
|
||||
"ThumbnailValleyOfSpires",
|
||||
"https://raw.githubusercontent.com/GraphiteEditor/Graphite/master/demo-artwork/valley-of-spires.graphite",
|
||||
),
|
||||
(
|
||||
"Just a Potted Cactus",
|
||||
"ThumbnailJustAPottedCactus",
|
||||
"https://raw.githubusercontent.com/GraphiteEditor/Graphite/master/demo-artwork/just-a-potted-cactus.graphite",
|
||||
),
|
||||
];
|
||||
|
||||
let image_widgets = artwork
|
||||
.into_iter()
|
||||
.map(|(_, thumbnail, _)| ImageLabel::new(thumbnail.to_string()).width(Some("256px".into())).widget_holder())
|
||||
.collect();
|
||||
|
||||
let button_widgets = artwork
|
||||
.into_iter()
|
||||
.map(|(label, _, url)| {
|
||||
TextButton::new(label)
|
||||
.min_width(256)
|
||||
.on_update(|_| {
|
||||
DialogMessage::CloseDialogAndThen {
|
||||
followups: vec![FrontendMessage::TriggerFetchAndOpenDocument { url: url.to_string() }.into()],
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Demo Artwork".to_string()).bold(true).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row { widgets: image_widgets },
|
||||
LayoutGroup::Row { widgets: button_widgets },
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,12 @@ mod about_graphite_dialog;
|
|||
mod close_all_documents_dialog;
|
||||
mod close_document_dialog;
|
||||
mod coming_soon_dialog;
|
||||
mod demo_artwork_dialog;
|
||||
mod error_dialog;
|
||||
|
||||
pub use about_graphite_dialog::AboutGraphiteDialog;
|
||||
pub use close_all_documents_dialog::CloseAllDocumentsDialog;
|
||||
pub use close_document_dialog::CloseDocumentDialog;
|
||||
pub use coming_soon_dialog::ComingSoonDialog;
|
||||
pub use demo_artwork_dialog::DemoArtworkDialog;
|
||||
pub use error_dialog::ErrorDialog;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ pub enum FrontendMessage {
|
|||
document: String,
|
||||
name: String,
|
||||
},
|
||||
TriggerFetchAndOpenDocument {
|
||||
url: String,
|
||||
},
|
||||
TriggerFontLoad {
|
||||
font: Font,
|
||||
#[serde(rename = "isDefault")]
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconLabel(_) => {}
|
||||
Widget::ImageLabel(_) => {}
|
||||
Widget::InvisibleStandinInput(invisible) => {
|
||||
let callback_message = (invisible.on_update.callback)(&());
|
||||
responses.add(callback_message);
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ impl LayoutGroup {
|
|||
Widget::FontInput(x) => &mut x.tooltip,
|
||||
Widget::IconButton(x) => &mut x.tooltip,
|
||||
Widget::IconLabel(x) => &mut x.tooltip,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip,
|
||||
Widget::LayerReferenceInput(x) => &mut x.tooltip,
|
||||
Widget::NumberInput(x) => &mut x.tooltip,
|
||||
Widget::OptionalInput(x) => &mut x.tooltip,
|
||||
|
|
@ -438,6 +439,7 @@ pub enum Widget {
|
|||
FontInput(FontInput),
|
||||
IconButton(IconButton),
|
||||
IconLabel(IconLabel),
|
||||
ImageLabel(ImageLabel),
|
||||
InvisibleStandinInput(InvisibleStandinInput),
|
||||
LayerReferenceInput(LayerReferenceInput),
|
||||
NumberInput(NumberInput),
|
||||
|
|
@ -516,6 +518,7 @@ impl DiffUpdate {
|
|||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::TextButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconLabel(_)
|
||||
| Widget::ImageLabel(_)
|
||||
| Widget::CurveInput(_)
|
||||
| Widget::InvisibleStandinInput(_)
|
||||
| Widget::PivotAssist(_)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,18 @@ pub struct IconLabel {
|
|||
pub tooltip: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)]
|
||||
pub struct ImageLabel {
|
||||
#[widget_builder(constructor)]
|
||||
pub image: String,
|
||||
|
||||
pub width: Option<String>,
|
||||
|
||||
pub height: Option<String>,
|
||||
|
||||
pub tooltip: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, WidgetBuilder, specta::Type)]
|
||||
pub struct Separator {
|
||||
pub direction: SeparatorDirection,
|
||||
|
|
|
|||
|
|
@ -49,10 +49,17 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||
},
|
||||
MenuBarEntry {
|
||||
label: "Open…".into(),
|
||||
icon: Some("Folder".into()),
|
||||
shortcut: action_keys!(PortfolioMessageDiscriminant::OpenDocument),
|
||||
action: MenuBarEntry::create_action(|_| PortfolioMessage::OpenDocument.into()),
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Open Demo Artwork…".into(),
|
||||
icon: Some("Image".into()),
|
||||
action: MenuBarEntry::create_action(|_| DialogMessage::RequestDemoArtworkDialog.into()),
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
],
|
||||
vec![
|
||||
MenuBarEntry {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
{
|
||||
"extends": "@parcel/config-default",
|
||||
"transformers": {
|
||||
"*.svelte": [
|
||||
"parcel-transformer-svelte3-plus"
|
||||
],
|
||||
"*.svg": [
|
||||
"...",
|
||||
"@parcel/transformer-inline-string"
|
||||
],
|
||||
"*.svelte": [
|
||||
"parcel-transformer-svelte3-plus"
|
||||
"*.png, *.jpg": [
|
||||
"...",
|
||||
"@parcel/transformer-inline-string"
|
||||
]
|
||||
},
|
||||
"optimizers": {
|
||||
"*.png, *.jpg": [
|
||||
"@parcel/optimizer-data-url"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 189 B |
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -34,17 +34,18 @@
|
|||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/config-default": "^2.8.3",
|
||||
"@parcel/packager-raw-url": "^2.8.3",
|
||||
"@parcel/transformer-inline-string": "^2.8.3",
|
||||
"@parcel/transformer-webmanifest": "^2.8.3",
|
||||
"@parcel/config-default": "^2.9.3",
|
||||
"@parcel/packager-raw-url": "^2.9.3",
|
||||
"@parcel/optimizer-data-url": "^2.9.3",
|
||||
"@parcel/transformer-inline-string": "^2.9.3",
|
||||
"@parcel/transformer-webmanifest": "^2.9.3",
|
||||
"@types/license-checker-webpack-plugin": "^0.2.1",
|
||||
"@types/node": "^18.16.2",
|
||||
"@types/webpack": "^5.28.1",
|
||||
"buffer": "^5.7.1",
|
||||
"concurrently": "^8.0.1",
|
||||
"license-checker-webpack-plugin": "^0.2.1",
|
||||
"parcel": "^2.8.3",
|
||||
"parcel": "^2.9.3",
|
||||
"parcel-transformer-svelte3-plus": "^0.2.9",
|
||||
"postcss": "^8.4.23",
|
||||
"process": "^0.11.10",
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@
|
|||
height: auto;
|
||||
}
|
||||
|
||||
.image-label {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.panic-buttons-row {
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
import TextAreaInput from "@graphite/components/widgets/inputs/TextAreaInput.svelte";
|
||||
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
|
||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||
import ImageLabel from "@graphite/components/widgets/labels/ImageLabel.svelte";
|
||||
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||
import { getContext } from "svelte";
|
||||
|
|
@ -120,6 +121,10 @@
|
|||
{#if iconLabel}
|
||||
<IconLabel {...exclude(iconLabel)} />
|
||||
{/if}
|
||||
{@const imageLabel = narrowWidgetProps(component.props, "ImageLabel")}
|
||||
{#if imageLabel}
|
||||
<ImageLabel {...exclude(imageLabel)} />
|
||||
{/if}
|
||||
{@const layerReferenceInput = narrowWidgetProps(component.props, "LayerReferenceInput")}
|
||||
{#if layerReferenceInput}
|
||||
<LayerReferenceInput {...exclude(layerReferenceInput)} on:value={({ detail }) => updateLayout(index, detail)} />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import { IMAGE_BASE64_STRINGS } from "@graphite/utility-functions/images";
|
||||
|
||||
let className = "";
|
||||
export { className as class };
|
||||
export let classes: Record<string, boolean> = {};
|
||||
|
||||
export let image: string;
|
||||
export let width: string | undefined;
|
||||
export let height: string | undefined;
|
||||
export let tooltip: string | undefined = undefined;
|
||||
|
||||
$: extraClasses = Object.entries(classes)
|
||||
.flatMap((classAndState) => (classAndState[1] ? [classAndState[0]] : []))
|
||||
.join(" ");
|
||||
</script>
|
||||
|
||||
<img src={IMAGE_BASE64_STRINGS[image]} style:width style:height class={`image-label ${className} ${extraClasses}`.trim()} title={tooltip} alt="" />
|
||||
|
||||
<style lang="scss" global>
|
||||
.image-label {
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
+ .image-label {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -40,14 +40,6 @@
|
|||
|
||||
let tabElements: (LayoutRow | undefined)[] = [];
|
||||
|
||||
function newDocument() {
|
||||
editor.instance.newDocumentDialog();
|
||||
}
|
||||
|
||||
function openDocument() {
|
||||
editor.instance.documentOpen();
|
||||
}
|
||||
|
||||
function platformModifiers(reservedKey: boolean): LayoutKeysGroup {
|
||||
// TODO: Remove this by properly feeding these keys from a layout provided by the backend
|
||||
|
||||
|
|
@ -128,7 +120,7 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<TextButton label="New Document" icon="File" action={() => newDocument()} />
|
||||
<TextButton label="New Document" icon="File" action={() => editor.instance.newDocumentDialog()} />
|
||||
</td>
|
||||
<td>
|
||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(true), { key: "KeyN", label: "N" }]]} />
|
||||
|
|
@ -136,12 +128,17 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<TextButton label="Open Document" icon="Folder" action={() => openDocument()} />
|
||||
<TextButton label="Open Document" icon="Folder" action={() => editor.instance.openDocument()} />
|
||||
</td>
|
||||
<td>
|
||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(false), { key: "KeyO", label: "O" }]]} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<TextButton label="Open Demo Artwork" icon="Image" action={() => editor.instance.demoArtworkDialog()} />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
|
|
|
|||
|
|
@ -5,3 +5,13 @@ declare module "*.svg" {
|
|||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.png" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.jpg" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import { type Editor } from "@graphite/wasm-communication/editor";
|
|||
import {
|
||||
type FrontendDocumentDetails,
|
||||
TriggerCopyToClipboardBlobUrl,
|
||||
TriggerFetchAndOpenDocument,
|
||||
TriggerDownloadBlobUrl,
|
||||
TriggerDownloadRaster,
|
||||
TriggerDownloadTextFile,
|
||||
TriggerImaginateCheckServerStatus,
|
||||
TriggerImport,
|
||||
TriggerOpenDocument,
|
||||
TriggerRasterizeRegionBelowLayer,
|
||||
|
|
@ -45,6 +45,19 @@ export function createPortfolioState(editor: Editor) {
|
|||
return state;
|
||||
})
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(TriggerFetchAndOpenDocument, async (triggerFetchAndOpenDocument) => {
|
||||
try {
|
||||
const url = new URL(triggerFetchAndOpenDocument.url);
|
||||
const data = await fetch(url);
|
||||
|
||||
const filename = url.pathname.split("/").pop() || "Untitled";
|
||||
const content = await data.text();
|
||||
|
||||
editor.instance.openDocumentFile(filename, content);
|
||||
} catch {
|
||||
editor.instance.errorDialog("Failed to open document", "The file could not be reached over the internet. You may be offline, or it may be missing.");
|
||||
}
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(TriggerOpenDocument, async () => {
|
||||
const extension = editor.instance.fileSaveSuffix();
|
||||
const data = await upload(extension, "text");
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ import NodeBlur from "@graphite-frontend/assets/icon-16px-solid/node-blur.svg";
|
|||
import NodeBrushwork from "@graphite-frontend/assets/icon-16px-solid/node-brushwork.svg";
|
||||
import NodeColorCorrection from "@graphite-frontend/assets/icon-16px-solid/node-color-correction.svg";
|
||||
import NodeGradient from "@graphite-frontend/assets/icon-16px-solid/node-gradient.svg";
|
||||
import NodeImage from "@graphite-frontend/assets/icon-16px-solid/node-image.svg";
|
||||
import Image from "@graphite-frontend/assets/icon-16px-solid/image.svg";
|
||||
import NodeImaginate from "@graphite-frontend/assets/icon-16px-solid/node-imaginate.svg";
|
||||
import NodeMagicWand from "@graphite-frontend/assets/icon-16px-solid/node-magic-wand.svg";
|
||||
import NodeMask from "@graphite-frontend/assets/icon-16px-solid/node-mask.svg";
|
||||
|
|
@ -176,7 +176,7 @@ const SOLID_16PX = {
|
|||
NodeBrushwork: { svg: NodeBrushwork, size: 16 },
|
||||
NodeColorCorrection: { svg: NodeColorCorrection, size: 16 },
|
||||
NodeGradient: { svg: NodeGradient, size: 16 },
|
||||
NodeImage: { svg: NodeImage, size: 16 },
|
||||
Image: { svg: Image, size: 16 },
|
||||
NodeImaginate: { svg: NodeImaginate, size: 16 },
|
||||
NodeMagicWand: { svg: NodeMagicWand, size: 16 },
|
||||
NodeMask: { svg: NodeMask, size: 16 },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/* eslint-disable import/first */
|
||||
|
||||
// Demo artwork
|
||||
import ThumbnailJustAPottedCactus from "@graphite-frontend/assets/images/demo-artwork/thumbnail-just-a-potted-cactus.png";
|
||||
import ThumbnailValleyOfSpires from "@graphite-frontend/assets/images/demo-artwork/thumbnail-valley-of-spires.png";
|
||||
|
||||
const DEMO_ARTWORK = {
|
||||
ThumbnailJustAPottedCactus,
|
||||
ThumbnailValleyOfSpires,
|
||||
} as const;
|
||||
|
||||
// All images
|
||||
const IMAGE_LIST = {
|
||||
...DEMO_ARTWORK,
|
||||
} as const;
|
||||
|
||||
// Exported images and types
|
||||
export const IMAGES: ImageDefinitionType<typeof IMAGE_LIST> = IMAGE_LIST;
|
||||
export const IMAGE_BASE64_STRINGS = Object.fromEntries(Object.entries(IMAGES).map(([name, data]) => [name, data]));
|
||||
|
||||
// See `icons.ts` for explanation about how this works
|
||||
type EvaluateType<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
type ImageDefinitionType<T extends Record<string, string>> = EvaluateType<{ [key in keyof T]: string }>;
|
||||
|
|
@ -89,6 +89,21 @@ export function createEditor() {
|
|||
// Subscriptions: Allows subscribing to messages in JS that are sent from the WASM backend
|
||||
const subscriptions: SubscriptionRouter = createSubscriptionRouter();
|
||||
|
||||
// Check if the URL hash fragment has any demo artwork to be loaded
|
||||
(async () => {
|
||||
const demoArtwork = window.location.hash.trim().match(/#demo\/(.*)/)?.[1];
|
||||
if (!demoArtwork) return;
|
||||
|
||||
try {
|
||||
const url = new URL(`https://raw.githubusercontent.com/GraphiteEditor/Graphite/master/demo-artwork/${demoArtwork}.graphite`);
|
||||
const data = await fetch(url);
|
||||
|
||||
const filename = url.pathname.split("/").pop() || "Untitled";
|
||||
const content = await data.text();
|
||||
instance.openDocumentFile(filename, content);
|
||||
} catch {}
|
||||
})();
|
||||
|
||||
return {
|
||||
raw,
|
||||
instance,
|
||||
|
|
|
|||
|
|
@ -513,6 +513,10 @@ export class TriggerLoadAutoSaveDocuments extends JsMessage { }
|
|||
|
||||
export class TriggerLoadPreferences extends JsMessage { }
|
||||
|
||||
export class TriggerFetchAndOpenDocument extends JsMessage {
|
||||
readonly url!: string;
|
||||
}
|
||||
|
||||
export class TriggerOpenDocument extends JsMessage { }
|
||||
|
||||
export class TriggerImport extends JsMessage { }
|
||||
|
|
@ -874,6 +878,19 @@ export class IconLabel extends WidgetProps {
|
|||
tooltip!: string | undefined;
|
||||
}
|
||||
|
||||
export class ImageLabel extends WidgetProps {
|
||||
image!: IconName;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
width!: string | undefined;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
height!: string | undefined;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
tooltip!: string | undefined;
|
||||
}
|
||||
|
||||
export class LayerReferenceInput extends WidgetProps {
|
||||
@Transform(({ value }: { value: BigUint64Array | undefined }) => (value ? String(value) : undefined))
|
||||
value!: string | undefined;
|
||||
|
|
@ -1120,6 +1137,7 @@ const widgetSubTypes = [
|
|||
{ value: FontInput, name: "FontInput" },
|
||||
{ value: IconButton, name: "IconButton" },
|
||||
{ value: IconLabel, name: "IconLabel" },
|
||||
{ value: ImageLabel, name: "ImageLabel" },
|
||||
{ value: LayerReferenceInput, name: "LayerReferenceInput" },
|
||||
{ value: NumberInput, name: "NumberInput" },
|
||||
{ value: OptionalInput, name: "OptionalInput" },
|
||||
|
|
@ -1367,6 +1385,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
DisplayRemoveEditableTextbox,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerCopyToClipboardBlobUrl,
|
||||
TriggerFetchAndOpenDocument,
|
||||
TriggerDownloadBlobUrl,
|
||||
TriggerDownloadRaster,
|
||||
TriggerDownloadTextFile,
|
||||
|
|
|
|||
|
|
@ -308,12 +308,18 @@ impl JsEditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = documentOpen)]
|
||||
pub fn document_open(&self) {
|
||||
#[wasm_bindgen(js_name = openDocument)]
|
||||
pub fn open_document(&self) {
|
||||
let message = PortfolioMessage::OpenDocument;
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = demoArtworkDialog)]
|
||||
pub fn demo_artwork_dialog(&self) {
|
||||
let message = DialogMessage::RequestDemoArtworkDialog;
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = openDocumentFile)]
|
||||
pub fn open_document_file(&self, document_name: String, document_serialized_content: String) {
|
||||
let message = PortfolioMessage::OpenDocumentFile {
|
||||
|
|
|
|||
Loading…
Reference in New Issue