// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/capture_mode/folder_selection_dialog_controller.h" #include #include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/strings/grit/ash_strings.h" #include "ash/style/ash_color_id.h" #include "ash/style/ash_color_provider.h" #include "base/files/file_path.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window_observer.h" #include "ui/base/l10n/l10n_util.h" #include "ui/events/event.h" #include "ui/shell_dialogs/select_file_dialog.h" #include "ui/shell_dialogs/select_file_policy.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/transient_window_manager.h" namespace ash { namespace { class NullSelectFolderPolicy : public ui::SelectFilePolicy { public: NullSelectFolderPolicy() = default; ~NullSelectFolderPolicy() override = default; // ui::SelectFileDialog: bool CanOpenSelectFileDialog() override { return true; } void SelectFileDenied() override {} }; // Returns true if |event| is targeting a window in the subtree rooted at // |window|. bool IsEventTargetingWindowInSubtree(const ui::Event* event, const aura::Window* window) { DCHECK(window); auto* target = static_cast(event->target()); return window->Contains(target); } } // namespace FolderSelectionDialogController::FolderSelectionDialogController( Delegate* delegate, aura::Window* root) : delegate_(delegate), // The SettingBubbleContainer was chosen since it's below the virtual // keyboard container, and therefore users can use the VK to interact with // this dialog e.g. to rename a folder. dialog_background_dimmer_( root->GetChildById(kShellWindowId_SettingBubbleContainer)), select_folder_dialog_(ui::SelectFileDialog::Create( /*listener=*/this, std::make_unique())) { DCHECK(delegate_); DCHECK(root); DCHECK(root->IsRootWindow()); auto* owner = dialog_background_dimmer_.window(); owner->SetId(kShellWindowId_CaptureModeFolderSelectionDialogOwner); window_observation_.Observe(wm::TransientWindowManager::GetOrCreate(owner)); dialog_background_dimmer_.SetDimColor(kColorAshShieldAndBase40); owner->Show(); select_folder_dialog_->SelectFile( ui::SelectFileDialog::SELECT_FOLDER, l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_SAVE_TO_DIALOG_TITLE), /*default_path=*/base::FilePath(), /*file_types=*/nullptr, /*file_type_index=*/0, /*default_extension=*/base::FilePath::StringType(), /*owning_window=*/owner, /*params=*/nullptr); } FolderSelectionDialogController::~FolderSelectionDialogController() { if (select_folder_dialog_) select_folder_dialog_->ListenerDestroyed(); } bool FolderSelectionDialogController::ShouldConsumeEvent( const ui::Event* event) const { if (!dialog_window_) return true; if (IsEventTargetingWindowInSubtree(event, dialog_window_)) return false; // The event maybe targeting a virtual keyboard window that is being used to // interact with the dialog. In this case the event should not be consumed. auto* keyboard_ui_controller = keyboard::KeyboardUIController::Get(); DCHECK(keyboard_ui_controller); if (!keyboard_ui_controller->IsKeyboardVisible()) return true; auto* keyboard_window = keyboard_ui_controller->GetKeyboardWindow(); if (!keyboard_window || keyboard_window->GetRootWindow() != dialog_window_->GetRootWindow()) { return true; } return !IsEventTargetingWindowInSubtree(event, keyboard_window); } void FolderSelectionDialogController::FileSelected(const base::FilePath& path, int index, void* params) { did_user_select_a_folder_ = true; delegate_->OnFolderSelected(path); } void FolderSelectionDialogController::OnTransientChildAdded( aura::Window* window, aura::Window* transient) { DCHECK_EQ(window, dialog_background_dimmer_.window()); DCHECK(!dialog_window_); dialog_window_ = transient; // The dialog should never resize, minimize or maximize. auto* widget = views::Widget::GetWidgetForNativeWindow(dialog_window_); DCHECK(widget); views::WidgetDelegate* widget_delegate = widget->widget_delegate(); DCHECK(widget_delegate); widget_delegate->SetCanResize(false); widget_delegate->SetCanMinimize(false); widget_delegate->SetCanMaximize(false); delegate_->OnSelectionWindowAdded(); if (on_dialog_window_added_callback_for_test_) std::move(on_dialog_window_added_callback_for_test_).Run(); } void FolderSelectionDialogController::OnTransientChildRemoved( aura::Window* window, aura::Window* transient) { DCHECK_EQ(window, dialog_background_dimmer_.window()); DCHECK(dialog_window_); DCHECK_EQ(transient, dialog_window_); dialog_window_ = nullptr; delegate_->OnSelectionWindowClosed(); // |this| will be deleted after the above call. } } // namespace ash