mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-08-17 03:40:23 +03:00
feat(core): add include_image
macro (#9959)
This commit is contained in:
parent
586a816e62
commit
5b769948a8
5
.changes/include-image-macro-codegen.md
Normal file
5
.changes/include-image-macro-codegen.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-codegen": "patch:feat"
|
||||
---
|
||||
|
||||
Add `include_image_codegen` function to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
|
7
.changes/include-image-macro.md
Normal file
7
.changes/include-image-macro.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
"tauri": "patch:feat"
|
||||
"tauri-utils": "patch:feat"
|
||||
"tauri-macros": "patch:feat"
|
||||
---
|
||||
|
||||
Add `include_image` macro to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
|
@ -25,7 +25,10 @@ use tauri_utils::platform::Target;
|
||||
use tauri_utils::plugin::GLOBAL_API_SCRIPT_FILE_LIST_PATH;
|
||||
use tauri_utils::tokens::{map_lit, str_lit};
|
||||
|
||||
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
|
||||
use crate::embedded_assets::{
|
||||
ensure_out_dir, AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsResult,
|
||||
};
|
||||
use crate::image::{ico_icon, image_icon, png_icon, raw_icon};
|
||||
|
||||
const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json";
|
||||
const CAPABILITIES_FILE_NAME: &str = "capabilities.json";
|
||||
@ -65,7 +68,7 @@ fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut Csp
|
||||
|
||||
fn map_core_assets(
|
||||
options: &AssetOptions,
|
||||
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
|
||||
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> EmbeddedAssetsResult<()> {
|
||||
let csp = options.csp;
|
||||
let dangerous_disable_asset_csp_modification =
|
||||
options.dangerous_disable_asset_csp_modification.clone();
|
||||
@ -92,7 +95,7 @@ fn map_core_assets(
|
||||
fn map_isolation(
|
||||
_options: &AssetOptions,
|
||||
dir: PathBuf,
|
||||
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
|
||||
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> EmbeddedAssetsResult<()> {
|
||||
// create the csp for the isolation iframe styling now, to make the runtime less complex
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE);
|
||||
@ -129,7 +132,7 @@ fn map_isolation(
|
||||
}
|
||||
|
||||
/// Build a `tauri::Context` for including in application code.
|
||||
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let ContextData {
|
||||
dev,
|
||||
config,
|
||||
@ -201,17 +204,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
quote!(#assets)
|
||||
};
|
||||
|
||||
let out_dir = {
|
||||
let out_dir = std::env::var("OUT_DIR")
|
||||
.map_err(|_| EmbeddedAssetsError::OutDir)
|
||||
.map(PathBuf::from)
|
||||
.and_then(|p| p.canonicalize().map_err(|_| EmbeddedAssetsError::OutDir))?;
|
||||
|
||||
// make sure that our output directory is created
|
||||
std::fs::create_dir_all(&out_dir).map_err(|_| EmbeddedAssetsError::OutDir)?;
|
||||
|
||||
out_dir
|
||||
};
|
||||
let out_dir = ensure_out_dir()?;
|
||||
|
||||
let default_window_icon = {
|
||||
if target == Target::Windows {
|
||||
@ -223,7 +216,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
"icons/icon.ico",
|
||||
);
|
||||
if icon_path.exists() {
|
||||
ico_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
ico_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
} else {
|
||||
let icon_path = find_icon(
|
||||
&config,
|
||||
@ -231,7 +224,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
|i| i.ends_with(".png"),
|
||||
"icons/icon.png",
|
||||
);
|
||||
png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
png_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
}
|
||||
} else {
|
||||
// handle default window icons for Unix targets
|
||||
@ -241,7 +234,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
|i| i.ends_with(".png"),
|
||||
"icons/icon.png",
|
||||
);
|
||||
png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
png_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
}
|
||||
};
|
||||
|
||||
@ -260,7 +253,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
"icons/icon.png",
|
||||
);
|
||||
}
|
||||
raw_icon(&out_dir, icon_path)?
|
||||
raw_icon(&out_dir, &icon_path)?
|
||||
} else {
|
||||
quote!(::std::option::Option::None)
|
||||
};
|
||||
@ -289,18 +282,8 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
let with_tray_icon_code = if target.is_desktop() {
|
||||
if let Some(tray) = &config.app.tray_icon {
|
||||
let tray_icon_icon_path = config_parent.join(&tray.icon_path);
|
||||
let ext = tray_icon_icon_path.extension();
|
||||
if ext.map_or(false, |e| e == "ico") {
|
||||
ico_icon(&root, &out_dir, tray_icon_icon_path)
|
||||
image_icon(&root, &out_dir, &tray_icon_icon_path)
|
||||
.map(|i| quote!(context.set_tray_icon(Some(#i));))?
|
||||
} else if ext.map_or(false, |e| e == "png") {
|
||||
png_icon(&root, &out_dir, tray_icon_icon_path)
|
||||
.map(|i| quote!(context.set_tray_icon(Some(#i));))?
|
||||
} else {
|
||||
quote!(compile_error!(
|
||||
"The tray icon extension must be either `.ico` or `.png`."
|
||||
))
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
}
|
||||
@ -504,122 +487,18 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}))
|
||||
}
|
||||
|
||||
fn ico_icon<P: AsRef<Path>>(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
path: P,
|
||||
) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
let path = path.as_ref();
|
||||
let bytes = std::fs::read(path)
|
||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||
.to_vec();
|
||||
let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))
|
||||
.unwrap_or_else(|e| panic!("failed to parse icon {}: {}", path.display(), e));
|
||||
let entry = &icon_dir.entries()[0];
|
||||
let rgba = entry
|
||||
.decode()
|
||||
.unwrap_or_else(|e| panic!("failed to decode icon {}: {}", path.display(), e))
|
||||
.rgba_data()
|
||||
.to_vec();
|
||||
let width = entry.width();
|
||||
let height = entry.height();
|
||||
|
||||
let icon_file_name = path.file_name().unwrap();
|
||||
let out_path = out_dir.join(icon_file_name);
|
||||
write_if_changed(&out_path, &rgba).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_file_name = icon_file_name.to_str().unwrap();
|
||||
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
let path = path.as_ref();
|
||||
let bytes = std::fs::read(path)
|
||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||
.to_vec();
|
||||
|
||||
let out_path = out_dir.join(path.file_name().unwrap());
|
||||
write_if_changed(&out_path, &bytes).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_path = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
let icon = quote!(::std::option::Option::Some(
|
||||
include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_path)).to_vec()
|
||||
));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
fn png_icon<P: AsRef<Path>>(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
path: P,
|
||||
) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
let path = path.as_ref();
|
||||
let bytes = std::fs::read(path)
|
||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||
.to_vec();
|
||||
let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
|
||||
let mut reader = decoder
|
||||
.read_info()
|
||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
|
||||
|
||||
let (color_type, _) = reader.output_color_type();
|
||||
|
||||
if color_type != png::ColorType::Rgba {
|
||||
panic!("icon {} is not RGBA", path.display());
|
||||
}
|
||||
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
while let Ok(Some(row)) = reader.next_row() {
|
||||
buffer.extend(row.data());
|
||||
}
|
||||
let width = reader.info().width;
|
||||
let height = reader.info().height;
|
||||
|
||||
let icon_file_name = path.file_name().unwrap();
|
||||
let out_path = out_dir.join(icon_file_name);
|
||||
write_if_changed(&out_path, &buffer).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_file_name = icon_file_name.to_str().unwrap();
|
||||
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
if let Ok(curr) = std::fs::read(out_path) {
|
||||
if curr == data {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut out_file = File::create(out_path)?;
|
||||
out_file.write_all(data)
|
||||
}
|
||||
|
||||
fn find_icon<F: Fn(&&String) -> bool>(
|
||||
fn find_icon(
|
||||
config: &Config,
|
||||
config_parent: &Path,
|
||||
predicate: F,
|
||||
predicate: impl Fn(&&String) -> bool,
|
||||
default: &str,
|
||||
) -> PathBuf {
|
||||
let icon_path = config
|
||||
.bundle
|
||||
.icon
|
||||
.iter()
|
||||
.find(|i| predicate(i))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| default.to_string());
|
||||
.find(predicate)
|
||||
.map(AsRef::as_ref)
|
||||
.unwrap_or(default);
|
||||
config_parent.join(icon_path)
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ pub enum EmbeddedAssetsError {
|
||||
#[error("invalid prefix {prefix} used while including path {path}")]
|
||||
PrefixInvalid { prefix: PathBuf, path: PathBuf },
|
||||
|
||||
#[error("invalid extension {extension} used for image {path}, must be `ico` or `png`")]
|
||||
InvalidImageExtension { extension: PathBuf, path: PathBuf },
|
||||
|
||||
#[error("failed to walk directory {path} because {error}")]
|
||||
Walkdir {
|
||||
path: PathBuf,
|
||||
@ -61,6 +64,8 @@ pub enum EmbeddedAssetsError {
|
||||
Version(#[from] semver::Error),
|
||||
}
|
||||
|
||||
pub type EmbeddedAssetsResult<T> = Result<T, EmbeddedAssetsError>;
|
||||
|
||||
/// Represent a directory of assets that are compressed and embedded.
|
||||
///
|
||||
/// This is the compile time generation of [`tauri_utils::assets::Assets`] from a directory. Assets
|
||||
@ -439,3 +444,14 @@ impl ToTokens for EmbeddedAssets {
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_out_dir() -> EmbeddedAssetsResult<PathBuf> {
|
||||
let out_dir = std::env::var("OUT_DIR")
|
||||
.map_err(|_| EmbeddedAssetsError::OutDir)
|
||||
.map(PathBuf::from)
|
||||
.and_then(|p| p.canonicalize().map_err(|_| EmbeddedAssetsError::OutDir))?;
|
||||
|
||||
// make sure that our output directory is created
|
||||
std::fs::create_dir_all(&out_dir).map_err(|_| EmbeddedAssetsError::OutDir)?;
|
||||
Ok(out_dir)
|
||||
}
|
||||
|
136
core/tauri-codegen/src/image.rs
Normal file
136
core/tauri-codegen/src/image.rs
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::embedded_assets::{ensure_out_dir, EmbeddedAssetsError, EmbeddedAssetsResult};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use std::path::Path;
|
||||
use syn::{punctuated::Punctuated, Ident, PathArguments, PathSegment, Token};
|
||||
|
||||
pub fn include_image_codegen(path: &Path) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let out_dir = ensure_out_dir()?;
|
||||
|
||||
let mut segments = Punctuated::new();
|
||||
segments.push(PathSegment {
|
||||
ident: Ident::new("tauri", Span::call_site()),
|
||||
arguments: PathArguments::None,
|
||||
});
|
||||
let root = syn::Path {
|
||||
leading_colon: Some(Token![::](Span::call_site())),
|
||||
segments,
|
||||
};
|
||||
|
||||
image_icon(&root.to_token_stream(), &out_dir, path)
|
||||
}
|
||||
|
||||
pub(crate) fn image_icon(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
path: &Path,
|
||||
) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let extension = path.extension().unwrap_or_default();
|
||||
if extension == "ico" {
|
||||
ico_icon(root, out_dir, path)
|
||||
} else if extension == "png" {
|
||||
png_icon(root, out_dir, path)
|
||||
} else {
|
||||
Err(EmbeddedAssetsError::InvalidImageExtension {
|
||||
extension: extension.into(),
|
||||
path: path.to_path_buf(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn raw_icon(out_dir: &Path, path: &Path) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let bytes =
|
||||
std::fs::read(path).unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
|
||||
|
||||
let out_path = out_dir.join(path.file_name().unwrap());
|
||||
write_if_changed(&out_path, &bytes).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_path = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
let icon = quote!(::std::option::Option::Some(
|
||||
include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_path)).to_vec()
|
||||
));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
pub(crate) fn ico_icon(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
path: &Path,
|
||||
) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let file = std::fs::File::open(path)
|
||||
.unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
|
||||
let icon_dir = ico::IconDir::read(file)
|
||||
.unwrap_or_else(|e| panic!("failed to parse icon {}: {}", path.display(), e));
|
||||
let entry = &icon_dir.entries()[0];
|
||||
let rgba = entry
|
||||
.decode()
|
||||
.unwrap_or_else(|e| panic!("failed to decode icon {}: {}", path.display(), e))
|
||||
.rgba_data()
|
||||
.to_vec();
|
||||
let width = entry.width();
|
||||
let height = entry.height();
|
||||
|
||||
let icon_file_name = path.file_name().unwrap();
|
||||
let out_path = out_dir.join(icon_file_name);
|
||||
write_if_changed(&out_path, &rgba).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_file_name = icon_file_name.to_str().unwrap();
|
||||
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
pub(crate) fn png_icon(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
path: &Path,
|
||||
) -> EmbeddedAssetsResult<TokenStream> {
|
||||
let file = std::fs::File::open(path)
|
||||
.unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
|
||||
let decoder = png::Decoder::new(file);
|
||||
let mut reader = decoder
|
||||
.read_info()
|
||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
|
||||
|
||||
let (color_type, _) = reader.output_color_type();
|
||||
|
||||
if color_type != png::ColorType::Rgba {
|
||||
panic!("icon {} is not RGBA", path.display());
|
||||
}
|
||||
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
while let Ok(Some(row)) = reader.next_row() {
|
||||
buffer.extend(row.data());
|
||||
}
|
||||
let width = reader.info().width;
|
||||
let height = reader.info().height;
|
||||
|
||||
let icon_file_name = path.file_name().unwrap();
|
||||
let out_path = out_dir.join(icon_file_name);
|
||||
write_if_changed(&out_path, &buffer).map_err(|error| EmbeddedAssetsError::AssetWrite {
|
||||
path: path.to_owned(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
let icon_file_name = icon_file_name.to_str().unwrap();
|
||||
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
pub(crate) fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
|
||||
if let Ok(curr) = std::fs::read(out_path) {
|
||||
if curr == data {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
std::fs::write(out_path, data)
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
)]
|
||||
|
||||
pub use self::context::{context_codegen, ContextData};
|
||||
pub use self::image::include_image_codegen;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
path::{Path, PathBuf},
|
||||
@ -22,6 +23,7 @@ use tauri_utils::platform::Target;
|
||||
|
||||
mod context;
|
||||
pub mod embedded_assets;
|
||||
mod image;
|
||||
#[doc(hidden)]
|
||||
pub mod vendor;
|
||||
|
||||
|
@ -1318,7 +1318,7 @@
|
||||
]
|
||||
},
|
||||
"iconPath": {
|
||||
"description": "Path to the default icon to use for the tray icon.",
|
||||
"description": "Path to the default icon to use for the tray icon.\n\nNote: this stores the image in raw pixels to the final binary, so keep the icon size (width and height) small or else it's going to bloat your final executable",
|
||||
"type": "string"
|
||||
},
|
||||
"iconAsTemplate": {
|
||||
|
@ -11,9 +11,12 @@
|
||||
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
||||
)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::context::ContextItems;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::parse_macro_input;
|
||||
use quote::quote;
|
||||
use syn::{parse2, parse_macro_input, LitStr};
|
||||
|
||||
mod command;
|
||||
mod menu;
|
||||
@ -151,3 +154,58 @@ pub fn do_menu_item(input: TokenStream) -> TokenStream {
|
||||
let tokens = parse_macro_input!(input as menu::DoMenuItemInput);
|
||||
menu::do_menu_item(tokens).into()
|
||||
}
|
||||
|
||||
/// Convert a .png or .ico icon to an Image
|
||||
/// for things like `tauri::tray::TrayIconBuilder` to consume,
|
||||
/// relative paths are resolved from `CARGO_MANIFEST_DIR`, not current file
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// const APP_ICON: Image<'_> = include_image!("./icons/32x32.png");
|
||||
///
|
||||
/// // then use it with tray
|
||||
/// TrayIconBuilder::new().icon(APP_ICON).build().unwrap();
|
||||
///
|
||||
/// // or with window
|
||||
/// WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
|
||||
/// .icon(APP_ICON)
|
||||
/// .unwrap()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // or with any other functions that takes `Image` struct
|
||||
/// ```
|
||||
///
|
||||
/// Note: this stores the image in raw pixels to the final binary,
|
||||
/// so keep the icon size (width and height) small
|
||||
/// or else it's going to bloat your final executable
|
||||
#[proc_macro]
|
||||
pub fn include_image(tokens: TokenStream) -> TokenStream {
|
||||
let path = match parse2::<LitStr>(tokens.into()) {
|
||||
Ok(path) => path,
|
||||
Err(err) => return err.into_compile_error().into(),
|
||||
};
|
||||
let path = PathBuf::from(path.value());
|
||||
let resolved_path = if path.is_relative() {
|
||||
if let Ok(base_dir) = std::env::var("CARGO_MANIFEST_DIR").map(PathBuf::from) {
|
||||
base_dir.join(path)
|
||||
} else {
|
||||
return quote!(compile_error!("$CARGO_MANIFEST_DIR is not defined")).into();
|
||||
}
|
||||
} else {
|
||||
path
|
||||
};
|
||||
if !resolved_path.exists() {
|
||||
let error_string = format!(
|
||||
"Provided Image path \"{}\" doesn't exists",
|
||||
resolved_path.display()
|
||||
);
|
||||
return quote!(compile_error!(#error_string)).into();
|
||||
}
|
||||
match tauri_codegen::include_image_codegen(&resolved_path).map_err(|error| error.to_string()) {
|
||||
Ok(output) => output,
|
||||
Err(error) => quote!(compile_error!(#error)),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -1798,6 +1798,10 @@ pub struct TrayIconConfig {
|
||||
/// Set an id for this tray icon so you can reference it later, defaults to `main`.
|
||||
pub id: Option<String>,
|
||||
/// Path to the default icon to use for the tray icon.
|
||||
///
|
||||
/// Note: this stores the image in raw pixels to the final binary,
|
||||
/// so keep the icon size (width and height) small
|
||||
/// or else it's going to bloat your final executable
|
||||
#[serde(alias = "icon-path")]
|
||||
pub icon_path: PathBuf,
|
||||
/// A Boolean value that determines whether the image represents a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc) image on macOS.
|
||||
|
@ -75,6 +75,7 @@ pub use resources::{Resource, ResourceId, ResourceTable};
|
||||
#[cfg(target_os = "ios")]
|
||||
#[doc(hidden)]
|
||||
pub use swift_rs;
|
||||
pub use tauri_macros::include_image;
|
||||
#[cfg(mobile)]
|
||||
pub use tauri_macros::mobile_entry_point;
|
||||
pub use tauri_macros::{command, generate_handler};
|
||||
|
@ -1318,7 +1318,7 @@
|
||||
]
|
||||
},
|
||||
"iconPath": {
|
||||
"description": "Path to the default icon to use for the tray icon.",
|
||||
"description": "Path to the default icon to use for the tray icon.\n\nNote: this stores the image in raw pixels to the final binary, so keep the icon size (width and height) small or else it's going to bloat your final executable",
|
||||
"type": "string"
|
||||
},
|
||||
"iconAsTemplate": {
|
||||
|
Loading…
Reference in New Issue
Block a user