blob: 8c616f6fdd7aeb374713da9328f682481ae3c57e [file] [log] [blame]
// 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 <memory>
#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_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<aura::Window*>(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<NullSelectFolderPolicy>())) {
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(
AshColorProvider::Get()->GetShieldLayerColor(
AshColorProvider::ShieldLayerType::kShield40));
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