Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
../../../flutter/shell/platform/common/text_editing_delta_unittests.cc
../../../flutter/shell/platform/common/text_input_model_unittests.cc
../../../flutter/shell/platform/common/text_range_unittests.cc
../../../flutter/shell/platform/common/windowing_unittests.cc
../../../flutter/shell/platform/darwin/Doxyfile
../../../flutter/shell/platform/darwin/common/availability_version_check_unittests.cc
../../../flutter/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm
Expand Down Expand Up @@ -408,6 +409,7 @@
../../../flutter/shell/platform/windows/direct_manipulation_unittests.cc
../../../flutter/shell/platform/windows/dpi_utils_unittests.cc
../../../flutter/shell/platform/windows/fixtures
../../../flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc
../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc
../../../flutter/shell/platform/windows/flutter_window_unittests.cc
../../../flutter/shell/platform/windows/flutter_windows_engine_unittests.cc
Expand All @@ -428,6 +430,7 @@
../../../flutter/shell/platform/windows/text_input_plugin_unittest.cc
../../../flutter/shell/platform/windows/window_proc_delegate_manager_unittests.cc
../../../flutter/shell/platform/windows/window_unittests.cc
../../../flutter/shell/platform/windows/windowing_handler_unittests.cc
../../../flutter/shell/platform/windows/windows_lifecycle_manager_unittests.cc
../../../flutter/shell/profiling/sampling_profiler_unittest.cc
../../../flutter/shell/testing
Expand Down
8 changes: 8 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -47454,6 +47454,8 @@ FILE: ../../../flutter/shell/platform/common/text_editing_delta.h
FILE: ../../../flutter/shell/platform/common/text_input_model.cc
FILE: ../../../flutter/shell/platform/common/text_input_model.h
FILE: ../../../flutter/shell/platform/common/text_range.h
FILE: ../../../flutter/shell/platform/common/windowing.cc
FILE: ../../../flutter/shell/platform/common/windowing.h
FILE: ../../../flutter/shell/platform/darwin/common/availability_version_check.cc
FILE: ../../../flutter/shell/platform/darwin/common/availability_version_check.h
FILE: ../../../flutter/shell/platform/darwin/common/buffer_conversions.h
Expand Down Expand Up @@ -48197,6 +48199,10 @@ FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_win
FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_windows.h
FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc
FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h
FILE: ../../../flutter/shell/platform/windows/flutter_host_window.cc
FILE: ../../../flutter/shell/platform/windows/flutter_host_window.h
FILE: ../../../flutter/shell/platform/windows/flutter_host_window_controller.cc
FILE: ../../../flutter/shell/platform/windows/flutter_host_window_controller.h
FILE: ../../../flutter/shell/platform/windows/flutter_window.cc
FILE: ../../../flutter/shell/platform/windows/flutter_window.h
FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc
Expand Down Expand Up @@ -48246,6 +48252,8 @@ FILE: ../../../flutter/shell/platform/windows/window_binding_handler_delegate.h
FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager.cc
FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager.h
FILE: ../../../flutter/shell/platform/windows/window_state.h
FILE: ../../../flutter/shell/platform/windows/windowing_handler.cc
FILE: ../../../flutter/shell/platform/windows/windowing_handler.h
FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.cc
FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.h
FILE: ../../../flutter/shell/platform/windows/windows_proc_table.cc
Expand Down
4 changes: 4 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,10 @@ struct Settings {
// If true, the UI thread is the platform thread on supported
// platforms.
bool merged_platform_ui_thread = true;

// Enable support for multiple windows. Ignored if not supported on the
// platform.
bool enable_multi_window = false;
};

} // namespace flutter
Expand Down
11 changes: 11 additions & 0 deletions shell/common/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,17 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.merged_platform_ui_thread = !command_line.HasOption(
FlagForSwitch(Switch::DisableMergedPlatformUIThread));

#if FML_OS_WIN
// Process the EnableMultiWindow switch on Windows.
{
std::string enable_multi_window_value;
if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableMultiWindow),
&enable_multi_window_value)) {
settings.enable_multi_window = "true" == enable_multi_window_value;
}
}
#endif // FML_OS_WIN

return settings;
}

Expand Down
4 changes: 4 additions & 0 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ DEF_SWITCH(DisableMergedPlatformUIThread,
DEF_SWITCH(DisableAndroidSurfaceControl,
"disable-surface-control",
"Disable the SurfaceControl backed swapchain even when supported.")
DEF_SWITCH(EnableMultiWindow,
"enable-multi-window",
"Enable support for multiple windows. Ignored if not supported on "
"the platform.")
DEF_SWITCHES_END

void PrintUsage(const std::string& executable_name);
Expand Down
11 changes: 9 additions & 2 deletions shell/platform/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,13 @@ source_set("common_cpp_core") {
public = [
"geometry.h",
"path_utils.h",
"windowing.h",
]

sources = [ "path_utils.cc" ]
sources = [
"path_utils.cc",
"windowing.cc",
]

public_configs = [ "//flutter:config" ]
}
Expand All @@ -157,7 +161,10 @@ if (enable_unittests) {
executable("common_cpp_core_unittests") {
testonly = true

sources = [ "path_utils_unittests.cc" ]
sources = [
"path_utils_unittests.cc",
"windowing_unittests.cc",
]

deps = [
":common_cpp_core",
Expand Down
278 changes: 278 additions & 0 deletions shell/platform/common/windowing.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// Copyright 2013 The Flutter 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 "flutter/shell/platform/common/windowing.h"

#include <algorithm>
#include <iostream>

namespace flutter {

namespace {

WindowPoint offset_for(WindowSize const& size,
WindowPositioner::Anchor anchor) {
switch (anchor) {
case WindowPositioner::Anchor::top_left:
return {0, 0};
case WindowPositioner::Anchor::top:
return {-size.width / 2, 0};
case WindowPositioner::Anchor::top_right:
return {-1 * size.width, 0};
case WindowPositioner::Anchor::left:
return {0, -size.height / 2};
case WindowPositioner::Anchor::center:
return {-size.width / 2, -size.height / 2};
case WindowPositioner::Anchor::right:
return {-1 * size.width, -size.height / 2};
case WindowPositioner::Anchor::bottom_left:
return {0, -1 * size.height};
case WindowPositioner::Anchor::bottom:
return {-size.width / 2, -1 * size.height};
case WindowPositioner::Anchor::bottom_right:
return {-1 * size.width, -1 * size.height};
default:
std::cerr << "Unknown anchor value: " << static_cast<int>(anchor) << '\n';
std::abort();
}
}

WindowPoint anchor_position_for(WindowRectangle const& rect,
WindowPositioner::Anchor anchor) {
switch (anchor) {
case WindowPositioner::Anchor::top_left:
return rect.top_left;
case WindowPositioner::Anchor::top:
return rect.top_left + WindowPoint{rect.size.width / 2, 0};
case WindowPositioner::Anchor::top_right:
return rect.top_left + WindowPoint{rect.size.width, 0};
case WindowPositioner::Anchor::left:
return rect.top_left + WindowPoint{0, rect.size.height / 2};
case WindowPositioner::Anchor::center:
return rect.top_left +
WindowPoint{rect.size.width / 2, rect.size.height / 2};
case WindowPositioner::Anchor::right:
return rect.top_left + WindowPoint{rect.size.width, rect.size.height / 2};
case WindowPositioner::Anchor::bottom_left:
return rect.top_left + WindowPoint{0, rect.size.height};
case WindowPositioner::Anchor::bottom:
return rect.top_left + WindowPoint{rect.size.width / 2, rect.size.height};
case WindowPositioner::Anchor::bottom_right:
return rect.top_left + WindowPoint{rect.size.width, rect.size.height};
default:
std::cerr << "Unknown anchor value: " << static_cast<int>(anchor) << '\n';
std::abort();
}
}

WindowPoint constrain_to(WindowRectangle const& r, WindowPoint const& p) {
return {std::clamp(p.x, r.top_left.x, r.top_left.x + r.size.width),
std::clamp(p.y, r.top_left.y, r.top_left.y + r.size.height)};
}

WindowPositioner::Anchor flip_anchor_x(WindowPositioner::Anchor anchor) {
switch (anchor) {
case WindowPositioner::Anchor::top_left:
return WindowPositioner::Anchor::top_right;
case WindowPositioner::Anchor::top_right:
return WindowPositioner::Anchor::top_left;
case WindowPositioner::Anchor::left:
return WindowPositioner::Anchor::right;
case WindowPositioner::Anchor::right:
return WindowPositioner::Anchor::left;
case WindowPositioner::Anchor::bottom_left:
return WindowPositioner::Anchor::bottom_right;
case WindowPositioner::Anchor::bottom_right:
return WindowPositioner::Anchor::bottom_left;
default:
return anchor;
}
}

WindowPositioner::Anchor flip_anchor_y(WindowPositioner::Anchor anchor) {
switch (anchor) {
case WindowPositioner::Anchor::top_left:
return WindowPositioner::Anchor::bottom_left;
case WindowPositioner::Anchor::top:
return WindowPositioner::Anchor::bottom;
case WindowPositioner::Anchor::top_right:
return WindowPositioner::Anchor::bottom_right;
case WindowPositioner::Anchor::bottom_left:
return WindowPositioner::Anchor::top_left;
case WindowPositioner::Anchor::bottom:
return WindowPositioner::Anchor::top;
case WindowPositioner::Anchor::bottom_right:
return WindowPositioner::Anchor::top_right;
default:
return anchor;
}
}

WindowPoint flip_offset_x(WindowPoint const& p) {
return {-1 * p.x, p.y};
}

WindowPoint flip_offset_y(WindowPoint const& p) {
return {p.x, -1 * p.y};
}

} // namespace

WindowRectangle PlaceWindow(WindowPositioner const& positioner,
WindowSize child_size,
WindowRectangle const& anchor_rect,
WindowRectangle const& parent_rect,
WindowRectangle const& output_rect) {
WindowRectangle default_result;

{
WindowPoint const result =
constrain_to(parent_rect, anchor_position_for(
anchor_rect, positioner.parent_anchor) +
positioner.offset) +
offset_for(child_size, positioner.child_anchor);

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}

default_result = WindowRectangle{result, child_size};
}

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::flip_x)) {
WindowPoint const result =
constrain_to(parent_rect,
anchor_position_for(
anchor_rect, flip_anchor_x(positioner.parent_anchor)) +
flip_offset_x(positioner.offset)) +
offset_for(child_size, flip_anchor_x(positioner.child_anchor));

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}
}

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::flip_y)) {
WindowPoint const result =
constrain_to(parent_rect,
anchor_position_for(
anchor_rect, flip_anchor_y(positioner.parent_anchor)) +
flip_offset_y(positioner.offset)) +
offset_for(child_size, flip_anchor_y(positioner.child_anchor));

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}
}

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::flip_x) &&
static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::flip_y)) {
WindowPoint const result =
constrain_to(
parent_rect,
anchor_position_for(anchor_rect, flip_anchor_x(flip_anchor_y(
positioner.parent_anchor))) +
flip_offset_x(flip_offset_y(positioner.offset))) +
offset_for(child_size,
flip_anchor_x(flip_anchor_y(positioner.child_anchor)));

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}
}

{
WindowPoint result =
constrain_to(parent_rect, anchor_position_for(
anchor_rect, positioner.parent_anchor) +
positioner.offset) +
offset_for(child_size, positioner.child_anchor);

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::slide_x)) {
int const left_overhang = result.x - output_rect.top_left.x;
int const right_overhang =
(result.x + child_size.width) -
(output_rect.top_left.x + output_rect.size.width);

if (left_overhang < 0) {
result.x -= left_overhang;
} else if (right_overhang > 0) {
result.x -= right_overhang;
}
}

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::slide_y)) {
int const top_overhang = result.y - output_rect.top_left.y;
int const bot_overhang =
(result.y + child_size.height) -
(output_rect.top_left.y + output_rect.size.height);

if (top_overhang < 0) {
result.y -= top_overhang;
} else if (bot_overhang > 0) {
result.y -= bot_overhang;
}
}

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}
}

{
WindowPoint result =
constrain_to(parent_rect, anchor_position_for(
anchor_rect, positioner.parent_anchor) +
positioner.offset) +
offset_for(child_size, positioner.child_anchor);

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::resize_x)) {
int const left_overhang = result.x - output_rect.top_left.x;
int const right_overhang =
(result.x + child_size.width) -
(output_rect.top_left.x + output_rect.size.width);

if (left_overhang < 0) {
result.x -= left_overhang;
child_size.width += left_overhang;
}

if (right_overhang > 0) {
child_size.width -= right_overhang;
}
}

if (static_cast<int>(positioner.constraint_adjustment) &
static_cast<int>(WindowPositioner::ConstraintAdjustment::resize_y)) {
int const top_overhang = result.y - output_rect.top_left.y;
int const bot_overhang =
(result.y + child_size.height) -
(output_rect.top_left.y + output_rect.size.height);

if (top_overhang < 0) {
result.y -= top_overhang;
child_size.height += top_overhang;
}

if (bot_overhang > 0) {
child_size.height -= bot_overhang;
}
}

if (output_rect.contains({result, child_size})) {
return WindowRectangle{result, child_size};
}
}

return default_result;
}

} // namespace flutter
Loading
Loading