From 0a6f8c2554a52313ad804cb1c0d5287b1fbe524b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Mon, 8 Dec 2025 18:36:44 +0200 Subject: [PATCH] [macOS] Prefer user specified file extensions over OS preferred one. --- platform/macos/display_server_macos.mm | 4 +-- platform/macos/godot_open_save_delegate.h | 4 ++- platform/macos/godot_open_save_delegate.mm | 29 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index c6eb9a5647f..5bc7e4b3231 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -1060,7 +1060,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, Vector files; String url; url.append_utf8([[[panel URL] path] UTF8String]); - files.push_back(url); + files.push_back([panel_delegate validateFilename:url]); if (callback.is_valid()) { if (p_options_in_cb) { Variant v_result = true; @@ -1179,7 +1179,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, for (NSUInteger i = 0; i != [urls count]; ++i) { String url; url.append_utf8([[[urls objectAtIndex:i] path] UTF8String]); - files.push_back(url); + files.push_back([panel_delegate validateFilename:url]); } if (callback.is_valid()) { if (p_options_in_cb) { diff --git a/platform/macos/godot_open_save_delegate.h b/platform/macos/godot_open_save_delegate.h index a3682b6fb61..558ab23be32 100644 --- a/platform/macos/godot_open_save_delegate.h +++ b/platform/macos/godot_open_save_delegate.h @@ -41,6 +41,7 @@ @interface GodotOpenSaveDelegate : NSObject { NSSavePanel *dialog; NSMutableArray *allowed_types; + Vector> preferred_types; HashMap ctr_ids; Dictionary options; @@ -51,7 +52,7 @@ } - (void)makeAccessoryView:(NSSavePanel *)p_panel filters:(const Vector &)p_filters options:(const TypedArray &)p_options; -- (void)setFileTypes:(NSMutableArray *)p_allowed_types; +- (void)setFileTypes:(NSMutableArray *)p_allowed_types pref:(const Vector> &)p_preftypes; - (void)popupOptionAction:(id)p_sender; - (void)popupCheckAction:(id)p_sender; - (void)popupFileAction:(id)p_sender; @@ -60,5 +61,6 @@ - (int)setDefaultInt:(const String &)p_name value:(int)p_value; - (int)setDefaultBool:(const String &)p_name value:(bool)p_value; - (void)setRootPath:(const String &)p_root_path; +- (String)validateFilename:(const String &)p_path; @end diff --git a/platform/macos/godot_open_save_delegate.mm b/platform/macos/godot_open_save_delegate.mm index 346bfe1b649..63a9fbe0917 100644 --- a/platform/macos/godot_open_save_delegate.mm +++ b/platform/macos/godot_open_save_delegate.mm @@ -103,6 +103,7 @@ } NSMutableArray *new_allowed_types = [[NSMutableArray alloc] init]; + Vector> pref_types; bool has_type_popup = false; { NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]]; @@ -117,6 +118,7 @@ NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; for (int i = 0; i < p_filters.size(); i++) { Vector tokens = p_filters[i].split(";"); + Vector pref_type; if (tokens.size() >= 1) { String flt = tokens[0].strip_edges(); String mime = (tokens.size() >= 3) ? tokens[2].strip_edges() : String(); @@ -135,9 +137,11 @@ } if (ut) { [type_filters addObject:ut]; + pref_type.push_back(str.replace("*.", "").strip_edges()); } } else { [type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]]; + pref_type.push_back(str.replace("*.", "").strip_edges()); } } } @@ -158,6 +162,7 @@ if ([type_filters count] > 0) { NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : tokens[1].strip_edges()).utf8().get_data()]; [new_allowed_types addObject:type_filters]; + pref_types.push_back(pref_type); [popup addItemWithTitle:name_str]; } } @@ -171,6 +176,7 @@ } } else if (p_filters.size() == 1) { Vector tokens = p_filters[0].split(";"); + Vector pref_type; if (tokens.size() >= 1) { String flt = tokens[0].strip_edges(); String mime = (tokens.size() >= 3) ? tokens[2] : String(); @@ -189,9 +195,11 @@ } if (ut) { [type_filters addObject:ut]; + pref_type.push_back(str.replace("*.", "").strip_edges()); } } else { [type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]]; + pref_type.push_back(str.replace("*.", "").strip_edges()); } } } @@ -210,10 +218,11 @@ if ([type_filters count] > 0) { [new_allowed_types addObject:type_filters]; + pref_types.push_back(pref_type); } } } - [self setFileTypes:new_allowed_types]; + [self setFileTypes:new_allowed_types pref:pref_types]; } [base_view addSubview:view]; @@ -278,8 +287,9 @@ return cid; } -- (void)setFileTypes:(NSMutableArray *)p_allowed_types { +- (void)setFileTypes:(NSMutableArray *)p_allowed_types pref:(const Vector> &)p_preftypes { allowed_types = p_allowed_types; + preferred_types = p_preftypes; } - (instancetype)initWithDialog:(NSSavePanel *)p_dialog { @@ -346,6 +356,21 @@ root = p_root_path; } +- (String)validateFilename:(const String &)p_path { + if (@available(macOS 11, *)) { + if (allowed_types) { + NSMutableArray *type_filters = [allowed_types objectAtIndex:cur_index]; + UTType *ut = [type_filters objectAtIndex:0]; + String ext = String::utf8([[ut preferredFilenameExtension] UTF8String]); + Vector pref_ext = preferred_types[cur_index]; + if (!pref_ext.is_empty() && !pref_ext.has(ext) && p_path.has_extension(ext)) { + return p_path.get_basename() + "." + pref_ext[0]; + } + } + } + return p_path; +} + - (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError *_Nullable *)outError { if (root.is_empty()) { return YES;