Skip to content

Commit 8e0d604

Browse files
author
Eain Chen
committed
Add more details
Add explanations, examples. Add property 'Cancel', 'SuppressDefaultDialog'
1 parent 72b095e commit 8e0d604

1 file changed

Lines changed: 92 additions & 42 deletions

File tree

specs/ProgrammaticSaveAs.md

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,23 @@ will popup.
2020

2121
Additionally, we propose the `SaveAsRequested` event. You can register this
2222
event to block the default dialog and use the `SaveAsRequestedEventArgs`
23-
instead, to set your preferred save as path, save as type, and duplicate file replacement rule. In your client app, you can design your own UI to input
24-
these parameters. For HTML documents, we support 3 save as types: HTML_ONLY, SINGLE_FILE and COMPLETE. Non-HTML documents, must use DEFAULT, which will
23+
instead, to set your preferred save as path, save as type, and duplicate file
24+
replacement rule. In your client app, you can design your own UI to input
25+
these parameters. For HTML documents, we support 3 save as types: HTML_ONLY,
26+
SINGLE_FILE and COMPLETE. Non-HTML documents, must use DEFAULT, which will
2527
save the content as it is. This API has default values for all parameters,
2628
to perform the common save as operation.
2729

2830
# Examples
29-
## Win32 C++
31+
## Win32 C++
32+
### Add or Remove the Event Handler
33+
This example hides the default save as dialog and shows a customized dialog.
3034
```c++
31-
//! [ToggleSilent]
32-
// Turn on/off Silent SaveAs, which won't show the system default save as dialog.
33-
// This example hides the default save as dialog and shows a customized dialog.
34-
bool ScenarioSaveAs::ToggleSilent()
35+
bool ScenarioSaveAs::ToggleEventHandler()
3536
{
3637
if (!m_webView2Staging20)
3738
return false;
38-
if (m_silentSaveAs)
39+
if (m_hasSaveAsRequestedEventHandler)
3940
{
4041
// Unregister the handler for the `SaveAsRequested` event.
4142
m_webView2Staging20->remove_SaveAsRequested(m_saveAsRequestedToken);
@@ -50,14 +51,21 @@ bool ScenarioSaveAs::ToggleSilent()
5051
ICoreWebView2StagingSaveAsRequestedEventArgs* args) -> HRESULT
5152
{
5253
// Hide the system default save as dialog.
53-
CHECK_FAILURE(args->put_Handled(TRUE));
54-
54+
CHECK_FAILURE(args->put_SuppressDefaultDialog(TRUE));
55+
5556
auto showCustomizedDialog = [this, args]
5657
{
5758
// Preview the content mime type, optional
5859
wil::unique_cotaskmem_string mimeType;
5960
CHECK_FAILURE(args->get_ContentMimeType(&mimeType));
6061

62+
63+
// As an end developer, you can design your own dialog UI, or no UI at all.
64+
// You can ask the user information like file name, file extenstion, and etc.
65+
// Finally, concatenate and pass them into the event args
66+
//
67+
// This is a customized dialog example, the constructor returns after the
68+
// dialog interaction is completed by the end user.
6169
SaveAsDialog dialog(m_appWindow->GetMainWindow(), contentSaveTypes);
6270
if (dialog.confirmed)
6371
{
@@ -68,10 +76,15 @@ bool ScenarioSaveAs::ToggleSilent()
6876
args->put_ResultFilePath((LPCWSTR)dialog.path.c_str()));
6977
CHECK_FAILURE(args->put_SaveAsType(dialog.selectedType));
7078
CHECK_FAILURE(args->put_AllowReplace(dialog.allowReplace));
71-
72-
// Confirm to download, required
73-
CHECK_FAILURE(args->put_ConfirmToSave(TRUE));
7479
}
80+
else
81+
{
82+
// Save As cancelled from this customized dialog
83+
CHECK_FAILURE(args->put_Cancel(TRUE));
84+
}
85+
86+
// Indicate out parameters have been set.
87+
CHECK_FAILURE(args->put_Handled(TRUE));
7588
};
7689

7790
wil::com_ptr<ICoreWebView2Deferral> deferral;
@@ -88,17 +101,18 @@ bool ScenarioSaveAs::ToggleSilent()
88101
.Get(),
89102
&m_saveAsRequestedToken);
90103
}
91-
m_silentSaveAs = !m_silentSaveAs;
104+
m_hasSaveAsRequestedEventHandler = !m_hasSaveAsRequestedEventHandler;
92105
MessageBox(
93106
m_appWindow->GetMainWindow(),
94-
(m_silentSaveAs ? L"Silent Save As Enabled" : L"Silent Save As Disabled"), L"Info",
107+
(m_hasSaveAsRequestedEventHandler ? L"Event Handler Added" : L"Event Handler Rremoved"), L"Info",
95108
MB_OK);
96109
return true;
97110
}
98-
//! [ToggleSilent]
111+
```
112+
### Programmatic Save As
113+
Call SaveContentAs method to trigger the programmatic save as.
114+
```c++
99115

100-
//! [ProgrammaticSaveAs]
101-
// Call SaveContentAs method to trigger the programmatic save as.
102116
bool ScenarioSaveAs::ProgrammaticSaveAs()
103117
{
104118
if (!m_webView2Staging20)
@@ -121,15 +135,15 @@ bool ScenarioSaveAs::ProgrammaticSaveAs()
121135
.Get());
122136
return true;
123137
}
124-
//! [ProgrammaticSaveAs]
125138
```
139+
126140
# API Details
127141
## Win32 C++
128142
```c++
129143
/// Specifies save as type selection options for `ICoreWebView2Staging20`,
130144
/// used in `SaveAsRequestedEventArgs`
131145
///
132-
/// When the source is a html page, allows to select `HTML_ONLY`,
146+
/// When the source is a html page, supports to select `HTML_ONLY`,
133147
/// `SINGLE_FILE`, `COMPLETE`; when the source is a non-html,
134148
/// only allows to select `DEFAULT`; otherwise, will deny the download
135149
/// and return `COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORTED`.
@@ -147,7 +161,7 @@ bool ScenarioSaveAs::ProgrammaticSaveAs()
147161
/// Save the page as mhtml
148162
COREWEBVIEW2_SAVE_AS_TYPE_SINGLE_FILE,
149163
/// Save the page as html, plus, download the page related source files in
150-
/// a folder
164+
/// a same name directory
151165
COREWEBVIEW2_SAVE_AS_TYPE_COMPLETE,
152166
} COREWEBVIEW2_SAVE_AS_TYPE;
153167

@@ -157,12 +171,24 @@ bool ScenarioSaveAs::ProgrammaticSaveAs()
157171
/// Programmatically open a system default save as dialog
158172
COREWEBVIEW2_SAVE_AS_OPEN_SYSTEM_DIALOG,
159173
/// Could not perform Save As because the destination file path is an invalid path.
174+
///
175+
/// It is considered as invalid when:
176+
/// the path is empty, a relativate path, the parent directory doesn't
177+
/// exist, or the path is a driectory.
178+
///
179+
/// Parent directory can be itself, if the path is root directory, or
180+
/// root disk. When the root doesn't exist, the path is invalid.
160181
COREWEBVIEW2_SAVE_AS_INVALID_PATH,
161182
/// Could not perform Save As because the destination file path already exists and
162183
/// replacing files was not allowed by the AllowReplace property.
163184
COREWEBVIEW2_SAVE_AS_FILE_ALREADY_EXISTS,
164185
/// Save as downloading not start as the `SAVE_AS_TYPE` selection not
165186
/// supported because of the content MIME type or system limits
187+
///
188+
/// MIME type limits please see the emun `COREWEBVIEW2_SAVE_AS_TYPE`
189+
///
190+
/// System limits might happen when select `HTML_ONLY` for an error page,
191+
/// select `COMPLETE` and WebView running in an App Container, etc.
166192
COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORTED,
167193
/// Did not perform Save As because the client side decided to cancel.
168194
COREWEBVIEW2_SAVE_AS_CANCELLED,
@@ -188,14 +214,14 @@ interface ICoreWebView2Staging20 : IUnknown {
188214
/// Add an event handler for the `SaveAsRequested` event. This event is raised
189215
/// when save as is triggered, programmatically or manually.
190216
///
191-
/// \snippet ScenarioSaveAs.cpp ToggleSilent
217+
/// \snippet ScenarioSaveAs.cpp ToggleEventHandler
192218
HRESULT add_SaveAsRequested(
193219
[in] ICoreWebView2StagingSaveAsRequestedEventHandler* eventHanlder,
194220
[out] EventRegistrationToken* token);
195221

196222
/// Remove an event handler previously added with `add_SaveAsRequested`.
197223
///
198-
/// \snippet ScenarioSaveAs.cpp ToggleSilent
224+
/// \snippet ScenarioSaveAs.cpp ToggleEventHandler
199225
HRESULT remove_SaveAsRequested(
200226
[in] EventRegistrationToken token);
201227
}
@@ -215,26 +241,39 @@ interface ICoreWebView2StagingSaveAsRequestedEventArgs : IUnknown {
215241
/// Get the Mime type of content to be saved
216242
[propget] HRESULT ContentMimeType([out, retval] LPWSTR* value);
217243

218-
/// Indicates if this event is a silent save as job. `Handled` as FALSE means
219-
/// save as handled by system default dialog; TRUE means a silent save as,
220-
/// will skip the system dialog.
244+
/// Indicates if pramameters in the event args has been set, TRUE means been set.
245+
///
246+
/// The default value is FALSE.
221247
///
222248
/// Set the `Handled` for save as
223-
[propput] HRESULT Handled ([in] BOOL handled);
249+
[propput] HRESULT Handled ([in] BOOL value);
224250

225251
/// Get the `Handled` for save as
226-
[propget] HRESULT Handled ([out, retval] BOOL* handled);
252+
[propget] HRESULT Handled ([out, retval] BOOL* value);
227253

228-
/// Indicates if a silent save as confirm to download, TRUE means confirm.
229-
/// when the event is invoked, the download will start. A programmatic call will
230-
/// return COREWEBVIEW2_SAVE_AS_STARTED as well. set it FASLE to cancel save as
231-
/// and will return COREWEBVIEW2_SAVE_AS_CANCELLED.
254+
/// Indicates if client side cancelled the silent save as, TRUE means cancelled.
255+
/// When the event is invoked, the download won't start. A programmatic call will
256+
/// return COREWEBVIEW2_SAVE_AS_CANCELLED as well.
232257
///
233-
/// Set the `ConfrimToSave` for save as
234-
[propput] HRESULT ConfirmToSave ([in] BOOL confirmToSave);
258+
/// The default value is FALSE.
259+
///
260+
/// Set the `Cancel` for save as
261+
[propput] HRESULT Cancel ([in] BOOL value);
235262

236-
/// Get the `ConfrimToSave` for save as
237-
[propget] HRESULT ConfirmToSave ([out, retval] BOOL* confirmToSave);
263+
/// Get the `Cancel` for save as
264+
[propget] HRESULT Cancel ([out, retval] BOOL* value);
265+
266+
/// Indicates if the system default dialog will be supressed, FALSE means
267+
/// save as default dialog will show; TRUE means a silent save as, will
268+
/// skip the system dialog.
269+
///
270+
/// The default value is FALSE.
271+
///
272+
/// Set the `SupressDefaultDialog`
273+
[propput] HRESULT SupressDefaultDialog([in] BOOL value);
274+
275+
/// Get the `SupressDefaultDialog`
276+
[propget] HRESULT SupressDefaultDialog([out, retval] BOOL* value);
238277

239278
/// Returns an `ICoreWebView2Deferral` object. This will defer showing the
240279
/// default Save As dialog and performing the Save As operation.
@@ -244,34 +283,45 @@ interface ICoreWebView2StagingSaveAsRequestedEventArgs : IUnknown {
244283
/// file name and extension. If `ResultFilePath` is not valid, e.g. root drive
245284
/// not exist, save as will be denied and return COREWEBVIEW2_SAVE_AS_INVALID_PATH.
246285
///
286+
/// When the download complete and success, a target file will be saved at this
287+
/// location. If the SAVE_AS_TYPE is `COMPLETE`, will be an additional directory
288+
/// with resources files. The directory has the same name as filename, at the same
289+
/// location.
290+
///
291+
/// The default value is a system suggested path, based on users' local environment.
292+
///
247293
/// Set the `ResultFilePath` for save as
248-
[propput] HRESULT ResultFilePath ([in] LPCWSTR resultFilePath);
294+
[propput] HRESULT ResultFilePath ([in] LPCWSTR value);
249295

250296
/// Get the `ResultFilePath` for save as
251-
[propget] HRESULT ResultFilePath ([out, retval] LPWSTR* resultFilePath);
297+
[propget] HRESULT ResultFilePath ([out, retval] LPWSTR* value);
252298

253299
/// `AllowReplace` allows you to control what happens when a file already
254300
/// exists in the file path to which the Save As operation is saving.
255301
/// Setting this TRUE allows existing files to be replaced.
256302
/// Settings this FALSE will not replace existing files and will return
257303
/// COREWEBVIEW2_SAVE_AS_FILE_ALREADY_EXISTS.
258304
///
305+
/// The default value is FALSE
306+
///
259307
/// Set if allowed to replace the old file if duplicate happens in the save as job
260-
[propput] HRESULT AllowReplace ([in] BOOL allowReplace);
308+
[propput] HRESULT AllowReplace ([in] BOOL value);
261309

262310
/// Get the duplicates replace rule for save as
263-
[propget] HRESULT AllowReplace ([out, retval] BOOL* allowReplace);
311+
[propget] HRESULT AllowReplace ([out, retval] BOOL* value);
264312

265313
/// How to save documents with different types. See the enum
266314
/// COREWEBVIEW2_SAVE_AS_TYPE for a description of the different options.
267315
/// If the type isn't allowed for the current document,
268316
/// COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORT will be returned from SaveContentAs.
269317
///
318+
/// The default value is COREWEBVIEW2_SAVE_AS_TYPE_DEFAULT
319+
///
270320
/// Set the content save as type for save as job
271-
[propput] HRESULT SaveAsType ([in] COREWEBVIEW2_SAVE_AS_TYPE type);
321+
[propput] HRESULT SaveAsType ([in] COREWEBVIEW2_SAVE_AS_TYPE value);
272322

273323
/// Get the content save as type for save as job
274-
[propget] HRESULT SaveAsType ([out, retval] COREWEBVIEW2_SAVE_AS_TYPE* type);
324+
[propget] HRESULT SaveAsType ([out, retval] COREWEBVIEW2_SAVE_AS_TYPE* value);
275325
}
276326

277327
/// Receive the result for `SaveContentAs` method

0 commit comments

Comments
 (0)