Rewrite dialog API with our simpler blocking dialog implementations.
Fixed #9, fixed #10.
This commit is contained in:
		
					parent
					
						
							
								d19e62d867
							
						
					
				
			
			
				commit
				
					
						2bb33d8b32
					
				
			
		
					 6 changed files with 216 additions and 217 deletions
				
			
		
							
								
								
									
										2
									
								
								atom.gyp
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								atom.gyp
									
										
									
									
									
								
							|  | @ -70,6 +70,8 @@ | |||
|       'browser/browser.h', | ||||
|       'browser/browser_mac.mm', | ||||
|       'browser/browser_observer.h', | ||||
|       'browser/file_dialog.h', | ||||
|       'browser/file_dialog_mac.mm', | ||||
|       'browser/message_box.h', | ||||
|       'browser/message_box_mac.mm', | ||||
|       'browser/native_window.cc', | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include "base/utf_string_conversions.h" | ||||
| #include "base/values.h" | ||||
| #include "browser/api/atom_api_window.h" | ||||
| #include "browser/file_dialog.h" | ||||
| #include "browser/message_box.h" | ||||
| #include "browser/native_window.h" | ||||
| 
 | ||||
|  | @ -23,6 +24,19 @@ base::FilePath V8ValueToFilePath(v8::Handle<v8::Value> path) { | |||
|   return base::FilePath::FromUTF8Unsafe(path_string); | ||||
| } | ||||
| 
 | ||||
| v8::Handle<v8::Value> FilePathToV8Value(const base::FilePath path) { | ||||
|   std::string path_string(path.AsUTF8Unsafe()); | ||||
|   return v8::String::New(path_string.data(), path_string.size()); | ||||
| } | ||||
| 
 | ||||
| void Initialize(v8::Handle<v8::Object> target) { | ||||
|   v8::HandleScope scope; | ||||
| 
 | ||||
|   NODE_SET_METHOD(target, "showMessageBox", ShowMessageBox); | ||||
|   NODE_SET_METHOD(target, "showOpenDialog", ShowOpenDialog); | ||||
|   NODE_SET_METHOD(target, "showSaveDialog", ShowSaveDialog); | ||||
| } | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| v8::Handle<v8::Value> ShowMessageBox(const v8::Arguments &args) { | ||||
|  | @ -50,153 +64,56 @@ v8::Handle<v8::Value> ShowMessageBox(const v8::Arguments &args) { | |||
|   return scope.Close(v8::Integer::New(chosen)); | ||||
| } | ||||
| 
 | ||||
| FileDialog::FileDialog(v8::Handle<v8::Object> wrapper) | ||||
|     : EventEmitter(wrapper), | ||||
|       dialog_(ui::SelectFileDialog::Create(this, NULL)) { | ||||
| } | ||||
| 
 | ||||
| FileDialog::~FileDialog() { | ||||
| } | ||||
| 
 | ||||
| void FileDialog::FileSelected(const base::FilePath& path, | ||||
|                               int index, void* params) { | ||||
|   int* id = static_cast<int*>(params); | ||||
| 
 | ||||
|   base::ListValue args; | ||||
|   args.AppendInteger(*id); | ||||
|   args.AppendString(path.value()); | ||||
| 
 | ||||
|   Emit("selected", &args); | ||||
| 
 | ||||
|   delete id; | ||||
| } | ||||
| 
 | ||||
| void FileDialog::MultiFilesSelected(const std::vector<base::FilePath>& files, | ||||
|                                     void* params) { | ||||
|   int* id = static_cast<int*>(params); | ||||
| 
 | ||||
|   base::ListValue args; | ||||
|   args.AppendInteger(*id); | ||||
|   for (size_t i = 0; i < files.size(); i++) | ||||
|     args.AppendString(files[i].value()); | ||||
| 
 | ||||
|   Emit("selected", &args); | ||||
| 
 | ||||
|   delete id; | ||||
| } | ||||
| 
 | ||||
| void FileDialog::FileSelectionCanceled(void* params) { | ||||
|   int* id = static_cast<int*>(params); | ||||
| 
 | ||||
|   base::ListValue args; | ||||
|   args.AppendInteger(*id); | ||||
| 
 | ||||
|   Emit("cancelled", &args); | ||||
| 
 | ||||
|   delete id; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| v8::Handle<v8::Value> FileDialog::New(const v8::Arguments &args) { | ||||
| v8::Handle<v8::Value> ShowOpenDialog(const v8::Arguments &args) { | ||||
|   v8::HandleScope scope; | ||||
| 
 | ||||
|   if (!args.IsConstructCall()) | ||||
|     return node::ThrowError("Require constructor call"); | ||||
|   if (!args[0]->IsString() ||  // title
 | ||||
|       !args[1]->IsString() ||  // default_path
 | ||||
|       !args[2]->IsNumber())    // properties
 | ||||
|     return node::ThrowTypeError("Bad argument"); | ||||
| 
 | ||||
|   new FileDialog(args.This()); | ||||
|   std::string title(*v8::String::Utf8Value(args[0])); | ||||
|   base::FilePath default_path(V8ValueToFilePath(args[1])); | ||||
|   int properties = args[2]->IntegerValue(); | ||||
| 
 | ||||
|   return args.This(); | ||||
|   std::vector<base::FilePath> paths; | ||||
|   if (!file_dialog::ShowOpenDialog(title, default_path, properties, &paths)) | ||||
|     return v8::Undefined(); | ||||
| 
 | ||||
|   v8::Handle<v8::Array> result = v8::Array::New(paths.size()); | ||||
|   for (size_t i = 0; i < paths.size(); ++i) | ||||
|     result->Set(i, FilePathToV8Value(paths[i])); | ||||
| 
 | ||||
|   return scope.Close(result); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| v8::Handle<v8::Value> FileDialog::SelectFile(const v8::Arguments &args) { | ||||
|   FileDialog* self = Unwrap<FileDialog>(args.This()); | ||||
|   if (!self) | ||||
|     return node::ThrowError("The FileDialog object is corrupted"); | ||||
| v8::Handle<v8::Value> ShowSaveDialog(const v8::Arguments &args) { | ||||
|   v8::HandleScope scope; | ||||
| 
 | ||||
|   if (!args[0]->IsObject() ||  // window
 | ||||
|       !args[1]->IsNumber() ||  // type
 | ||||
|       !args[2]->IsString() ||  // title
 | ||||
|       !args[3]->IsString() ||  // default_path
 | ||||
|       !args[4]->IsArray() ||   // file_types
 | ||||
|       !args[5]->IsNumber() ||  // file_type_index
 | ||||
|       !args[6]->IsString() ||  // default_extension
 | ||||
|       !args[7]->IsNumber())    // callback_id
 | ||||
|       !args[1]->IsString() ||  // title
 | ||||
|       !args[2]->IsString())    // default_path
 | ||||
|     return node::ThrowTypeError("Bad argument"); | ||||
| 
 | ||||
|   Window* window = Window::Unwrap<Window>(args[0]->ToObject()); | ||||
|   if (!window || !window->window()) | ||||
|     return node::ThrowError("Invalid window"); | ||||
| 
 | ||||
|   gfx::NativeWindow owning_window = window->window()->GetNativeWindow(); | ||||
|   std::string title(*v8::String::Utf8Value(args[1])); | ||||
|   base::FilePath default_path(V8ValueToFilePath(args[2])); | ||||
| 
 | ||||
|   int type = args[1]->IntegerValue(); | ||||
|   std::string title(*v8::String::Utf8Value(args[2])); | ||||
|   base::FilePath default_path(V8ValueToFilePath(args[3])); | ||||
|   base::FilePath path; | ||||
|   if (!file_dialog::ShowSaveDialog(window->window(), | ||||
|                                    title, | ||||
|                                    default_path, | ||||
|                                    &path)) | ||||
|     return v8::Undefined(); | ||||
| 
 | ||||
|   ui::SelectFileDialog::FileTypeInfo file_types; | ||||
|   FillTypeInfo(&file_types, v8::Handle<v8::Array>::Cast(args[4])); | ||||
| 
 | ||||
|   int file_type_index = args[5]->IntegerValue(); | ||||
|   std::string default_extension(*v8::String::Utf8Value(args[6])); | ||||
|   int callback_id = args[7]->IntegerValue(); | ||||
| 
 | ||||
|   self->dialog_->SelectFile( | ||||
|       (ui::SelectFileDialog::Type)(type), | ||||
|       UTF8ToUTF16(title), | ||||
|       default_path, | ||||
|       file_types.extensions.size() > 0 ? &file_types : NULL, | ||||
|       file_type_index, | ||||
|       default_extension, | ||||
|       owning_window, | ||||
|       new int(callback_id)); | ||||
| 
 | ||||
|   return v8::Undefined(); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void FileDialog::FillTypeInfo(ui::SelectFileDialog::FileTypeInfo* file_types, | ||||
|                               v8::Handle<v8::Array> v8_file_types) { | ||||
|   file_types->include_all_files = true; | ||||
|   file_types->support_drive = true; | ||||
| 
 | ||||
|   for (uint32_t i = 0; i < v8_file_types->Length(); ++i) { | ||||
|     v8::Handle<v8::Object> element = v8_file_types->Get(i)->ToObject(); | ||||
| 
 | ||||
|     std::string description(*v8::String::Utf8Value( | ||||
|         element->Get(v8::String::New("description")))); | ||||
|     file_types->extension_description_overrides.push_back( | ||||
|         UTF8ToUTF16(description)); | ||||
| 
 | ||||
|     std::vector<base::FilePath::StringType> extensions; | ||||
|     v8::Handle<v8::Array> v8_extensions = v8::Handle<v8::Array>::Cast( | ||||
|         element->Get(v8::String::New("extensions"))); | ||||
| 
 | ||||
|     for (uint32_t j = 0; j < v8_extensions->Length(); ++j) { | ||||
|       std::string extension(*v8::String::Utf8Value(v8_extensions->Get(j))); | ||||
|       extensions.push_back(extension); | ||||
|     } | ||||
|     file_types->extensions.push_back(extensions); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void FileDialog::Initialize(v8::Handle<v8::Object> target) { | ||||
|   v8::HandleScope scope; | ||||
| 
 | ||||
|   v8::Local<v8::FunctionTemplate> t(v8::FunctionTemplate::New(FileDialog::New)); | ||||
|   t->InstanceTemplate()->SetInternalFieldCount(1); | ||||
|   t->SetClassName(v8::String::NewSymbol("FileDialog")); | ||||
| 
 | ||||
|   NODE_SET_PROTOTYPE_METHOD(t, "selectFile", SelectFile); | ||||
| 
 | ||||
|   target->Set(v8::String::NewSymbol("FileDialog"), t->GetFunction()); | ||||
| 
 | ||||
|   NODE_SET_METHOD(target, "showMessageBox", ShowMessageBox); | ||||
|   return scope.Close(FilePathToV8Value(path)); | ||||
| } | ||||
| 
 | ||||
| }  // namespace api
 | ||||
| 
 | ||||
| }  // namespace atom
 | ||||
| 
 | ||||
| NODE_MODULE(atom_browser_dialog, atom::api::FileDialog::Initialize) | ||||
| NODE_MODULE(atom_browser_dialog, atom::api::Initialize) | ||||
|  |  | |||
|  | @ -5,42 +5,15 @@ | |||
| #ifndef ATOM_BROWSER_API_ATOM_API_DIALOG_H_ | ||||
| #define ATOM_BROWSER_API_ATOM_API_DIALOG_H_ | ||||
| 
 | ||||
| #include "browser/api/atom_api_event_emitter.h" | ||||
| #include "ui/shell_dialogs/select_file_dialog.h" | ||||
| #include "v8/include/v8.h" | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| namespace api { | ||||
| 
 | ||||
| v8::Handle<v8::Value> ShowMessageBox(const v8::Arguments &args); | ||||
| 
 | ||||
| class FileDialog : public EventEmitter, | ||||
|                    public ui::SelectFileDialog::Listener { | ||||
|  public: | ||||
|   virtual ~FileDialog(); | ||||
| 
 | ||||
|   static void Initialize(v8::Handle<v8::Object> target); | ||||
| 
 | ||||
|   // ui::SelectFileDialog::Listener implementations:
 | ||||
|   virtual void FileSelected(const base::FilePath& path, | ||||
|                             int index, void* params) OVERRIDE; | ||||
|   virtual void MultiFilesSelected( | ||||
|       const std::vector<base::FilePath>& files, void* params) OVERRIDE; | ||||
|   virtual void FileSelectionCanceled(void* params) OVERRIDE; | ||||
| 
 | ||||
|  private: | ||||
|   explicit FileDialog(v8::Handle<v8::Object> wrapper); | ||||
| 
 | ||||
|   static void FillTypeInfo(ui::SelectFileDialog::FileTypeInfo* file_types, | ||||
|                            v8::Handle<v8::Array> v8_file_types); | ||||
| 
 | ||||
|   static v8::Handle<v8::Value> New(const v8::Arguments &args); | ||||
|   static v8::Handle<v8::Value> SelectFile(const v8::Arguments &args); | ||||
| 
 | ||||
|   scoped_refptr<ui::SelectFileDialog> dialog_; | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(FileDialog); | ||||
| }; | ||||
| v8::Handle<v8::Value> ShowOpenDialog(const v8::Arguments &args); | ||||
| v8::Handle<v8::Value> ShowSaveDialog(const v8::Arguments &args); | ||||
| 
 | ||||
| }  // namespace api
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,72 +1,34 @@ | |||
| binding = process.atomBinding 'dialog' | ||||
| BrowserWindow = require 'browser_window' | ||||
| CallbacksRegistry = require 'callbacks_registry' | ||||
| EventEmitter = require('events').EventEmitter | ||||
| ipc = require 'ipc' | ||||
| 
 | ||||
| FileDialog = binding.FileDialog | ||||
| FileDialog.prototype.__proto__ = EventEmitter.prototype | ||||
| 
 | ||||
| callbacksRegistry = new CallbacksRegistry | ||||
| 
 | ||||
| fileDialog = new FileDialog | ||||
| 
 | ||||
| fileDialog.on 'selected', (event, callbackId, paths...) -> | ||||
|   callbacksRegistry.call callbackId, 'selected', paths... | ||||
|   callbacksRegistry.remove callbackId | ||||
| 
 | ||||
| fileDialog.on 'cancelled', (event, callbackId) -> | ||||
|   callbacksRegistry.call callbackId, 'cancelled' | ||||
|   callbacksRegistry.remove callbackId | ||||
| 
 | ||||
| validateOptions = (options) -> | ||||
|   return false unless typeof options is 'object' | ||||
| 
 | ||||
|   options.fileTypes = [] unless Array.isArray options.fileTypes | ||||
|   for type in options.fileTypes | ||||
|     return false unless typeof type is 'object' and | ||||
|                         typeof type.description is 'string' | ||||
|                         Array.isArray type.extensions | ||||
| 
 | ||||
|   options.defaultPath = '' unless options.defaultPath? | ||||
|   options.fileTypeIndex = 0 unless options.fileTypeIndex? | ||||
|   options.defaultExtension = '' unless options.defaultExtension? | ||||
|   true | ||||
| 
 | ||||
| selectFileWrap = (window, options, callback, type, title) -> | ||||
|   throw new TypeError('Need BrowserWindow object') unless window.constructor is BrowserWindow | ||||
| 
 | ||||
|   options = {} unless options? | ||||
|   options.type = type | ||||
|   options.title = title unless options.title? | ||||
| 
 | ||||
|   throw new TypeError('Bad arguments') unless validateOptions options | ||||
| 
 | ||||
|   callbackId = callbacksRegistry.add callback | ||||
| 
 | ||||
|   fileDialog.selectFile window, | ||||
|                         options.type, | ||||
|                         options.title, | ||||
|                         options.defaultPath, | ||||
|                         options.fileTypes, | ||||
|                         options.fileTypeIndex, | ||||
|                         options.defaultExtension, | ||||
|                         callbackId | ||||
| fileDialogProperties = | ||||
|   openFile: 1, openDirectory: 2, multiSelections: 4, createDirectory: 8 | ||||
| 
 | ||||
| messageBoxTypes = ['none', 'info', 'warning'] | ||||
| 
 | ||||
| module.exports = | ||||
|   openFolder: (args...) -> | ||||
|     selectFileWrap args..., 1, 'Open Folder' | ||||
|   showOpenDialog: (options) -> | ||||
|     options = title: 'Open', properties: ['openFile'] unless options? | ||||
|     options.properties = options.properties ? ['openFile'] | ||||
|     throw new TypeError('Properties need to be array') unless Array.isArray options.properties | ||||
| 
 | ||||
|   saveAs: (args...) -> | ||||
|     selectFileWrap args..., 2, 'Save As' | ||||
|     properties = 0 | ||||
|     for prop, value of fileDialogProperties | ||||
|       properties |= value if prop in options.properties | ||||
| 
 | ||||
|   openFile: (args...) -> | ||||
|     selectFileWrap args..., 3, 'Open File' | ||||
|     options.title = options.title ? '' | ||||
|     options.defaultPath = options.defaultPath ? '' | ||||
| 
 | ||||
|   openMultiFiles: (args...) -> | ||||
|     selectFileWrap args..., 4, 'Open Files' | ||||
|     binding.showOpenDialog options.title, options.defaultPath, properties | ||||
| 
 | ||||
|   showSaveDialog: (window, options) -> | ||||
|     throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow | ||||
|     options = title: 'Save' unless options? | ||||
| 
 | ||||
|     options.title = options.title ? '' | ||||
|     options.defaultPath = options.defaultPath ? '' | ||||
| 
 | ||||
|     binding.showSaveDialog window, options.title, options.defaultPath | ||||
| 
 | ||||
|   showMessageBox: (options) -> | ||||
|     options = type: 'none' unless options? | ||||
|  |  | |||
							
								
								
									
										38
									
								
								browser/file_dialog.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								browser/file_dialog.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| // Copyright (c) 2013 GitHub, Inc. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #ifndef BROWSER_FILE_DIALOG_H_ | ||||
| #define BROWSER_FILE_DIALOG_H_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "base/files/file_path.h" | ||||
| 
 | ||||
| namespace atom { | ||||
| class NativeWindow; | ||||
| } | ||||
| 
 | ||||
| namespace file_dialog { | ||||
| 
 | ||||
| enum FileDialogProperty { | ||||
|   FILE_DIALOG_OPEN_FILE        = 1, | ||||
|   FILE_DIALOG_OPEN_DIRECTORY   = 2, | ||||
|   FILE_DIALOG_MULTI_SELECTIONS = 4, | ||||
|   FILE_DIALOG_CREATE_DIRECTORY = 8, | ||||
| }; | ||||
| 
 | ||||
| bool ShowOpenDialog(const std::string& title, | ||||
|                     const base::FilePath& default_path, | ||||
|                     int properties, | ||||
|                     std::vector<base::FilePath>* paths); | ||||
| 
 | ||||
| bool ShowSaveDialog(atom::NativeWindow* window, | ||||
|                     const std::string& title, | ||||
|                     const base::FilePath& default_path, | ||||
|                     base::FilePath* path); | ||||
| 
 | ||||
| }  // namespace file_dialog
 | ||||
| 
 | ||||
| #endif  // BROWSER_FILE_DIALOG_H_
 | ||||
							
								
								
									
										107
									
								
								browser/file_dialog_mac.mm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								browser/file_dialog_mac.mm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| // Copyright (c) 2013 GitHub, Inc. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style license that can be | ||||
| // found in the LICENSE file. | ||||
| 
 | ||||
| #include "browser/file_dialog.h" | ||||
| 
 | ||||
| #import <Cocoa/Cocoa.h> | ||||
| #include <CoreServices/CoreServices.h> | ||||
| 
 | ||||
| #include "base/file_util.h" | ||||
| #include "base/strings/sys_string_conversions.h" | ||||
| #include "browser/native_window.h" | ||||
| 
 | ||||
| namespace file_dialog { | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| void SetupDialog(NSSavePanel* dialog, | ||||
|                  const std::string& title, | ||||
|                  const base::FilePath& default_path) { | ||||
|   if (!title.empty()) | ||||
|     [dialog setTitle:base::SysUTF8ToNSString(title)]; | ||||
| 
 | ||||
|   NSString* default_dir = nil; | ||||
|   NSString* default_filename = nil; | ||||
|   if (!default_path.empty()) { | ||||
|     if (file_util::DirectoryExists(default_path)) { | ||||
|       default_dir = base::SysUTF8ToNSString(default_path.value()); | ||||
|     } else { | ||||
|       default_dir = base::SysUTF8ToNSString(default_path.DirName().value()); | ||||
|       default_filename = | ||||
|           base::SysUTF8ToNSString(default_path.BaseName().value()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (default_dir) | ||||
|     [dialog setDirectoryURL:[NSURL fileURLWithPath:default_dir]]; | ||||
|   if (default_filename) | ||||
|     [dialog setNameFieldStringValue:default_filename]; | ||||
| 
 | ||||
|   [dialog setAllowsOtherFileTypes:YES]; | ||||
| } | ||||
| 
 | ||||
| }  // namespace | ||||
| 
 | ||||
| bool ShowOpenDialog(const std::string& title, | ||||
|                     const base::FilePath& default_path, | ||||
|                     int properties, | ||||
|                     std::vector<base::FilePath>* paths) { | ||||
|   DCHECK(paths); | ||||
|   NSOpenPanel* dialog = [NSOpenPanel openPanel]; | ||||
| 
 | ||||
|   SetupDialog(dialog, title, default_path); | ||||
| 
 | ||||
|   [dialog setCanChooseFiles:(properties & FILE_DIALOG_OPEN_FILE)]; | ||||
|   if (properties & FILE_DIALOG_OPEN_DIRECTORY) | ||||
|     [dialog setCanChooseDirectories:YES]; | ||||
|   if (properties & FILE_DIALOG_CREATE_DIRECTORY) | ||||
|     [dialog setCanCreateDirectories:YES]; | ||||
|   if (properties & FILE_DIALOG_MULTI_SELECTIONS) | ||||
|     [dialog setAllowsMultipleSelection:YES]; | ||||
| 
 | ||||
|   if ([dialog runModal] == NSFileHandlingPanelCancelButton) | ||||
|     return false; | ||||
| 
 | ||||
|   NSArray* urls = [dialog URLs]; | ||||
|   for (NSURL* url in urls) | ||||
|     if ([url isFileURL]) | ||||
|       paths->push_back(base::FilePath(base::SysNSStringToUTF8([url path]))); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool ShowSaveDialog(atom::NativeWindow* window, | ||||
|                     const std::string& title, | ||||
|                     const base::FilePath& default_path, | ||||
|                     base::FilePath* path) { | ||||
|   DCHECK(window); | ||||
|   DCHECK(path); | ||||
|   NSSavePanel* dialog = [NSSavePanel savePanel]; | ||||
| 
 | ||||
|   SetupDialog(dialog, title, default_path); | ||||
| 
 | ||||
|   [dialog setCanSelectHiddenExtension:YES]; | ||||
| 
 | ||||
|   __block bool result = false; | ||||
|   __block base::FilePath ret_path; | ||||
|   [dialog beginSheetModalForWindow:window->GetNativeWindow() | ||||
|                  completionHandler:^(NSInteger chosen) { | ||||
|     if (chosen == NSFileHandlingPanelCancelButton || | ||||
|         ![[dialog URL] isFileURL]) { | ||||
|       result = false; | ||||
|     } else { | ||||
|       result = true; | ||||
|       ret_path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path])); | ||||
|     } | ||||
| 
 | ||||
|     [NSApp stopModal]; | ||||
|   }]; | ||||
| 
 | ||||
|   [NSApp runModalForWindow:window->GetNativeWindow()]; | ||||
| 
 | ||||
|   *path = ret_path; | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| }  // namespace file_dialog | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheng Zhao
				Cheng Zhao