diff --git a/.changes/feat-add-dmg-config.md b/.changes/feat-add-dmg-config.md new file mode 100644 index 000000000000..8ae9528ed567 --- /dev/null +++ b/.changes/feat-add-dmg-config.md @@ -0,0 +1,6 @@ +--- +tauri-bundler: minor:feat +tauri-cli: minor:feat +--- + +Added support for DMG settings to `bundler > dmg`. Includes window size, background image and icon positions. diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index 1134acfa39b5..2626e0432cbf 100644 --- a/core/tauri-config-schema/schema.json +++ b/core/tauri-config-schema/schema.json @@ -139,6 +139,20 @@ "deb": { "files": {} }, + "dmg": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, "icon": [], "identifier": "", "macOS": { @@ -285,6 +299,20 @@ "deb": { "files": {} }, + "dmg": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, "icon": [], "identifier": "", "macOS": { @@ -1166,6 +1194,28 @@ } ] }, + "dmg": { + "description": "DMG-specific settings.", + "default": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, + "allOf": [ + { + "$ref": "#/definitions/DmgConfig" + } + ] + }, "macOS": { "description": "Configuration for the macOS bundles.", "default": { @@ -1495,6 +1545,113 @@ }, "additionalProperties": false }, + "DmgConfig": { + "description": "Configuration for Apple Disk Image (.dmg) bundles.\n\nSee more: https://tauri.app/v1/api/config#dmgconfig", + "type": "object", + "properties": { + "background": { + "description": "Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.", + "type": [ + "string", + "null" + ] + }, + "windowPosition": { + "description": "Position of volume window on screen.", + "anyOf": [ + { + "$ref": "#/definitions/Position" + }, + { + "type": "null" + } + ] + }, + "windowSize": { + "description": "Size of volume window.", + "default": { + "height": 400, + "width": 660 + }, + "allOf": [ + { + "$ref": "#/definitions/Size" + } + ] + }, + "appPosition": { + "description": "Position of app file on window.", + "default": { + "x": 180, + "y": 170 + }, + "allOf": [ + { + "$ref": "#/definitions/Position" + } + ] + }, + "applicationFolderPosition": { + "description": "Position of application folder on window.", + "default": { + "x": 480, + "y": 170 + }, + "allOf": [ + { + "$ref": "#/definitions/Position" + } + ] + } + }, + "additionalProperties": false + }, + "Position": { + "description": "Position coordinates struct.", + "type": "object", + "required": [ + "x", + "y" + ], + "properties": { + "x": { + "description": "X coordinate.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "y": { + "description": "Y coordinate.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Size": { + "description": "Size of the window.", + "type": "object", + "required": [ + "height", + "width" + ], + "properties": { + "width": { + "description": "Width of the window.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "height": { + "description": "Height of the window.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, "MacConfig": { "description": "Configuration for the macOS bundles.\n\nSee more: https://tauri.app/v1/api/config#macconfig", "type": "object", diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 003244d4f089..dc3366d7ed17 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -348,6 +348,81 @@ fn default_release() -> String { "1".into() } +/// Position coordinates struct. +#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Position { + /// X coordinate. + pub x: u32, + /// Y coordinate. + pub y: u32, +} + +/// Size of the window. +#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Size { + /// Width of the window. + pub width: u32, + /// Height of the window. + pub height: u32, +} + +/// Configuration for Apple Disk Image (.dmg) bundles. +/// +/// See more: https://tauri.app/v1/api/config#dmgconfig +#[skip_serializing_none] +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct DmgConfig { + /// Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`. + pub background: Option, + /// Position of volume window on screen. + pub window_position: Option, + /// Size of volume window. + #[serde(default = "dmg_window_size", alias = "window-size")] + pub window_size: Size, + /// Position of app file on window. + #[serde(default = "dmg_app_position", alias = "app-position")] + pub app_position: Position, + /// Position of application folder on window. + #[serde( + default = "dmg_application_folder_position", + alias = "application-folder-position" + )] + pub application_folder_position: Position, +} + +impl Default for DmgConfig { + fn default() -> Self { + Self { + background: None, + window_position: None, + window_size: dmg_window_size(), + app_position: dmg_app_position(), + application_folder_position: dmg_application_folder_position(), + } + } +} + +fn dmg_window_size() -> Size { + Size { + width: 660, + height: 400, + } +} + +fn dmg_app_position() -> Position { + Position { x: 180, y: 170 } +} + +fn dmg_application_folder_position() -> Position { + Position { x: 480, y: 170 } +} + fn de_minimum_system_version<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, @@ -810,6 +885,9 @@ pub struct BundleConfig { /// Configuration for the RPM bundle. #[serde(default)] pub rpm: RpmConfig, + /// DMG-specific settings. + #[serde(default)] + pub dmg: DmgConfig, /// Configuration for the macOS bundles. #[serde(rename = "macOS", default)] pub macos: MacConfig, @@ -3639,6 +3717,7 @@ mod build { let appimage = quote!(Default::default()); let deb = quote!(Default::default()); let rpm = quote!(Default::default()); + let dmg = quote!(Default::default()); let macos = quote!(Default::default()); let external_bin = opt_vec_str_lit(self.external_bin.as_ref()); let windows = &self.windows; @@ -3660,6 +3739,7 @@ mod build { appimage, deb, rpm, + dmg, macos, external_bin, windows @@ -4104,6 +4184,7 @@ mod test { appimage: Default::default(), deb: Default::default(), rpm: Default::default(), + dmg: Default::default(), macos: Default::default(), external_bin: None, windows: Default::default(), diff --git a/tooling/bundler/src/bundle.rs b/tooling/bundler/src/bundle.rs index b81657e7bb08..875a3231740d 100644 --- a/tooling/bundler/src/bundle.rs +++ b/tooling/bundler/src/bundle.rs @@ -20,8 +20,8 @@ use tauri_utils::display_path; pub use self::{ category::AppCategory, settings::{ - BundleBinary, BundleSettings, DebianSettings, MacOsSettings, PackageSettings, PackageType, - RpmSettings, Settings, SettingsBuilder, UpdaterSettings, + BundleBinary, BundleSettings, DebianSettings, DmgSettings, MacOsSettings, PackageSettings, + PackageType, Position, RpmSettings, Settings, SettingsBuilder, Size, UpdaterSettings, }, }; #[cfg(target_os = "macos")] diff --git a/tooling/bundler/src/bundle/macos/dmg.rs b/tooling/bundler/src/bundle/macos/dmg.rs index 3748a5cfe6de..94214e5a7d18 100644 --- a/tooling/bundler/src/bundle/macos/dmg.rs +++ b/tooling/bundler/src/bundle/macos/dmg.rs @@ -96,55 +96,90 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result< .output() .expect("Failed to chmod script"); - let mut args = vec![ + let dmg_settings = settings.dmg(); + + let app_position = &dmg_settings.app_position; + let application_folder_position = &dmg_settings.application_folder_position; + let window_size = &dmg_settings.window_size; + + let app_position_x = app_position.x.to_string(); + let app_position_y = app_position.y.to_string(); + let application_folder_position_x = application_folder_position.x.to_string(); + let application_folder_position_y = application_folder_position.y.to_string(); + let window_size_width = window_size.width.to_string(); + let window_size_height = window_size.height.to_string(); + + let mut bundle_dmg_cmd = Command::new(&bundle_script_path); + + bundle_dmg_cmd.args([ "--volname", product_name, "--icon", &bundle_file_name, - "180", - "170", + &app_position_x, + &app_position_y, "--app-drop-link", - "480", - "170", + &application_folder_position_x, + &application_folder_position_y, "--window-size", - "660", - "400", + &window_size_width, + &window_size_height, "--hide-extension", &bundle_file_name, - ]; + ]); - let icns_icon_path = - create_icns_file(&output_path, settings)?.map(|path| path.to_string_lossy().to_string()); + let window_position = dmg_settings + .window_position + .as_ref() + .map(|position| (position.x.to_string(), position.y.to_string())); + + if let Some(window_position) = &window_position { + bundle_dmg_cmd.arg("--window-pos"); + bundle_dmg_cmd.arg(&window_position.0); + bundle_dmg_cmd.arg(&window_position.1); + } + + let background_path = if let Some(background_path) = &dmg_settings.background { + Some(env::current_dir()?.join(background_path)) + } else { + None + }; + + if let Some(background_path) = &background_path { + bundle_dmg_cmd.arg("--background"); + bundle_dmg_cmd.arg(background_path); + } + + let icns_icon_path = create_icns_file(&output_path, settings)?; if let Some(icon) = &icns_icon_path { - args.push("--volicon"); - args.push(icon); + bundle_dmg_cmd.arg("--volicon"); + bundle_dmg_cmd.arg(icon); } - #[allow(unused_assignments)] - let mut license_path_ref = "".to_string(); - if let Some(license_path) = &settings.macos().license { - args.push("--eula"); - license_path_ref = env::current_dir()? - .join(license_path) - .to_string_lossy() - .to_string(); - args.push(&license_path_ref); + let license_path = if let Some(license_path) = &settings.macos().license { + Some(env::current_dir()?.join(license_path)) + } else { + None + }; + + if let Some(license_path) = &license_path { + bundle_dmg_cmd.arg("--eula"); + bundle_dmg_cmd.arg(license_path); } // Issue #592 - Building MacOS dmg files on CI // https://github.com/tauri-apps/tauri/issues/592 if let Some(value) = env::var_os("CI") { if value == "true" { - args.push("--skip-jenkins"); + bundle_dmg_cmd.arg("--skip-jenkins"); } } info!(action = "Running"; "bundle_dmg.sh"); // execute the bundle script - Command::new(&bundle_script_path) + bundle_dmg_cmd .current_dir(bundle_dir.clone()) - .args(args) .args(vec![dmg_name.as_str(), bundle_file_name.as_str()]) .output_ok() .context("error running bundle_dmg.sh")?; diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 0df2483992aa..449f94c2313e 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -237,6 +237,39 @@ pub struct RpmSettings { pub desktop_template: Option, } +/// Position coordinates struct. +#[derive(Clone, Debug, Default)] +pub struct Position { + /// X coordinate. + pub x: u32, + /// Y coordinate. + pub y: u32, +} + +/// Size of the window. +#[derive(Clone, Debug, Default)] +pub struct Size { + /// Width of the window. + pub width: u32, + /// Height of the window. + pub height: u32, +} + +/// The DMG bundle settings. +#[derive(Clone, Debug, Default)] +pub struct DmgSettings { + /// Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`. + pub background: Option, + /// Position of volume window on screen. + pub window_position: Option, + /// Size of volume window. + pub window_size: Size, + /// Position of app file on window. + pub app_position: Position, + /// Position of application folder on window. + pub application_folder_position: Position, +} + /// The macOS bundle settings. #[derive(Clone, Debug, Default)] pub struct MacOsSettings { @@ -485,6 +518,8 @@ pub struct BundleSettings { pub deb: DebianSettings, /// Rpm-specific settings. pub rpm: RpmSettings, + /// DMG-specific settings. + pub dmg: DmgSettings, /// MacOS-specific settings. pub macos: MacOsSettings, /// Updater configuration. @@ -949,6 +984,11 @@ impl Settings { &self.bundle_settings.rpm } + /// Returns the DMG settings. + pub fn dmg(&self) -> &DmgSettings { + &self.bundle_settings.dmg + } + /// Returns the MacOS settings. pub fn macos(&self) -> &MacOsSettings { &self.bundle_settings.macos diff --git a/tooling/bundler/src/error.rs b/tooling/bundler/src/error.rs index 6fbd4ca05803..ac19a7c329eb 100644 --- a/tooling/bundler/src/error.rs +++ b/tooling/bundler/src/error.rs @@ -67,6 +67,9 @@ pub enum Error { /// Couldn't find icons. #[error("Could not find Icon paths. Please make sure they exist in the tauri config JSON file")] IconPathError, + /// Couldn't find background file. + #[error("Could not find background file. Make sure it exists in the tauri config JSON file and extension is png/jpg/gif")] + BackgroundPathError, /// Error on path util operation. #[error("Path Error:`{0}`")] PathUtilError(String), diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index e026582e4134..be79087af014 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -4863,9 +4863,9 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f721bc2e55efb506a1a395a545cb76c2481fb023d33b51f0050e7888716281cf" +checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" dependencies = [ "windows-result", "windows-targets 0.52.5", @@ -4873,9 +4873,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 1134acfa39b5..2626e0432cbf 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -139,6 +139,20 @@ "deb": { "files": {} }, + "dmg": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, "icon": [], "identifier": "", "macOS": { @@ -285,6 +299,20 @@ "deb": { "files": {} }, + "dmg": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, "icon": [], "identifier": "", "macOS": { @@ -1166,6 +1194,28 @@ } ] }, + "dmg": { + "description": "DMG-specific settings.", + "default": { + "appPosition": { + "x": 180, + "y": 170 + }, + "applicationFolderPosition": { + "x": 480, + "y": 170 + }, + "windowSize": { + "height": 400, + "width": 660 + } + }, + "allOf": [ + { + "$ref": "#/definitions/DmgConfig" + } + ] + }, "macOS": { "description": "Configuration for the macOS bundles.", "default": { @@ -1495,6 +1545,113 @@ }, "additionalProperties": false }, + "DmgConfig": { + "description": "Configuration for Apple Disk Image (.dmg) bundles.\n\nSee more: https://tauri.app/v1/api/config#dmgconfig", + "type": "object", + "properties": { + "background": { + "description": "Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.", + "type": [ + "string", + "null" + ] + }, + "windowPosition": { + "description": "Position of volume window on screen.", + "anyOf": [ + { + "$ref": "#/definitions/Position" + }, + { + "type": "null" + } + ] + }, + "windowSize": { + "description": "Size of volume window.", + "default": { + "height": 400, + "width": 660 + }, + "allOf": [ + { + "$ref": "#/definitions/Size" + } + ] + }, + "appPosition": { + "description": "Position of app file on window.", + "default": { + "x": 180, + "y": 170 + }, + "allOf": [ + { + "$ref": "#/definitions/Position" + } + ] + }, + "applicationFolderPosition": { + "description": "Position of application folder on window.", + "default": { + "x": 480, + "y": 170 + }, + "allOf": [ + { + "$ref": "#/definitions/Position" + } + ] + } + }, + "additionalProperties": false + }, + "Position": { + "description": "Position coordinates struct.", + "type": "object", + "required": [ + "x", + "y" + ], + "properties": { + "x": { + "description": "X coordinate.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "y": { + "description": "Y coordinate.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Size": { + "description": "Size of the window.", + "type": "object", + "required": [ + "height", + "width" + ], + "properties": { + "width": { + "description": "Width of the window.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "height": { + "description": "Height of the window.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, "MacConfig": { "description": "Configuration for the macOS bundles.\n\nSee more: https://tauri.app/v1/api/config#macconfig", "type": "object", diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index eed9e1841c16..933d584d7bd8 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -28,8 +28,8 @@ use notify_debouncer_mini::new_debouncer; use serde::Deserialize; use shared_child::SharedChild; use tauri_bundler::{ - AppCategory, BundleBinary, BundleSettings, DebianSettings, MacOsSettings, PackageSettings, - RpmSettings, UpdaterSettings, WindowsSettings, + AppCategory, BundleBinary, BundleSettings, DebianSettings, DmgSettings, MacOsSettings, + PackageSettings, Position, RpmSettings, Size, UpdaterSettings, WindowsSettings, }; use tauri_utils::config::parse::is_configuration_file; @@ -1180,6 +1180,25 @@ fn tauri_config_to_bundle_settings( files: config.rpm.files, desktop_template: config.rpm.desktop_template, }, + dmg: DmgSettings { + background: config.dmg.background, + window_position: config.dmg.window_position.map(|window_position| Position { + x: window_position.x, + y: window_position.y, + }), + window_size: Size { + width: config.dmg.window_size.width, + height: config.dmg.window_size.height, + }, + app_position: Position { + x: config.dmg.app_position.x, + y: config.dmg.app_position.y, + }, + application_folder_position: Position { + x: config.dmg.application_folder_position.x, + y: config.dmg.application_folder_position.y, + }, + }, macos: MacOsSettings { frameworks: config.macos.frameworks, minimum_system_version: config.macos.minimum_system_version,