capture_mode_session.cc 108 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/capture_mode/capture_mode_session.h"
  5. #include <tuple>
  6. #include "ash/accessibility/accessibility_controller_impl.h"
  7. #include "ash/accessibility/magnifier/magnifier_glass.h"
  8. #include "ash/capture_mode/capture_label_view.h"
  9. #include "ash/capture_mode/capture_mode_bar_view.h"
  10. #include "ash/capture_mode/capture_mode_camera_controller.h"
  11. #include "ash/capture_mode/capture_mode_camera_preview_view.h"
  12. #include "ash/capture_mode/capture_mode_constants.h"
  13. #include "ash/capture_mode/capture_mode_controller.h"
  14. #include "ash/capture_mode/capture_mode_menu_group.h"
  15. #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
  16. #include "ash/capture_mode/capture_mode_settings_view.h"
  17. #include "ash/capture_mode/capture_mode_toggle_button.h"
  18. #include "ash/capture_mode/capture_mode_type_view.h"
  19. #include "ash/capture_mode/capture_mode_util.h"
  20. #include "ash/capture_mode/capture_window_observer.h"
  21. #include "ash/capture_mode/folder_selection_dialog_controller.h"
  22. #include "ash/capture_mode/user_nudge_controller.h"
  23. #include "ash/constants/ash_features.h"
  24. #include "ash/display/mouse_cursor_event_filter.h"
  25. #include "ash/display/screen_orientation_controller.h"
  26. #include "ash/display/window_tree_host_manager.h"
  27. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  28. #include "ash/projector/projector_controller_impl.h"
  29. #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
  30. #include "ash/public/cpp/shell_window_ids.h"
  31. #include "ash/resources/vector_icons/vector_icons.h"
  32. #include "ash/root_window_controller.h"
  33. #include "ash/shell.h"
  34. #include "ash/strings/grit/ash_strings.h"
  35. #include "ash/style/ash_color_provider.h"
  36. #include "ash/wm/mru_window_tracker.h"
  37. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  38. #include "ash/wm/window_dimmer.h"
  39. #include "base/bind.h"
  40. #include "base/callback_helpers.h"
  41. #include "base/check.h"
  42. #include "base/memory/ptr_util.h"
  43. #include "cc/paint/paint_flags.h"
  44. #include "ui/aura/client/aura_constants.h"
  45. #include "ui/aura/client/capture_client.h"
  46. #include "ui/aura/env.h"
  47. #include "ui/aura/window.h"
  48. #include "ui/aura/window_delegate.h"
  49. #include "ui/aura/window_observer.h"
  50. #include "ui/aura/window_tracker.h"
  51. #include "ui/base/cursor/cursor_factory.h"
  52. #include "ui/base/l10n/l10n_util.h"
  53. #include "ui/compositor/layer.h"
  54. #include "ui/compositor/layer_animation_element.h"
  55. #include "ui/compositor/layer_type.h"
  56. #include "ui/compositor/paint_recorder.h"
  57. #include "ui/compositor/scoped_layer_animation_settings.h"
  58. #include "ui/display/screen.h"
  59. #include "ui/display/types/display_constants.h"
  60. #include "ui/events/types/event_type.h"
  61. #include "ui/gfx/canvas.h"
  62. #include "ui/gfx/geometry/insets.h"
  63. #include "ui/gfx/geometry/point.h"
  64. #include "ui/gfx/geometry/rect.h"
  65. #include "ui/gfx/geometry/transform_util.h"
  66. #include "ui/gfx/paint_vector_icon.h"
  67. #include "ui/gfx/scoped_canvas.h"
  68. #include "ui/gfx/shadow_value.h"
  69. #include "ui/gfx/skia_paint_util.h"
  70. #include "ui/views/animation/animation_builder.h"
  71. #include "ui/views/background.h"
  72. #include "ui/views/controls/button/label_button.h"
  73. #include "ui/views/controls/label.h"
  74. #include "ui/views/widget/widget.h"
  75. #include "ui/wm/core/coordinate_conversion.h"
  76. #include "ui/wm/core/cursor_util.h"
  77. namespace ash {
  78. namespace {
  79. // The visual radius of the drag affordance circles which are shown while
  80. // resizing a drag region.
  81. constexpr int kAffordanceCircleRadiusDp = 4;
  82. // The hit radius of the drag affordance circles touch events.
  83. constexpr int kAffordanceCircleTouchHitRadiusDp = 16;
  84. // Capture region magnifier parameters.
  85. constexpr MagnifierGlass::Params kMagnifierParams{
  86. /*scale=*/2.f,
  87. /*radius=*/60,
  88. /*border_size=*/2,
  89. /*border_outline_thickness=*/0,
  90. /*border_color=*/SK_ColorWHITE,
  91. /*border_outline_color=*/SK_ColorTRANSPARENT,
  92. /*bottom_shadow=*/
  93. gfx::ShadowValue(gfx::Vector2d(0, 1),
  94. 2,
  95. SkColorSetARGB(0x4C, 0x00, 0x00, 0x00)),
  96. /*top_shadow=*/
  97. gfx::ShadowValue(gfx::Vector2d(0, 1),
  98. 3,
  99. SkColorSetARGB(0x26, 0x00, 0x00, 0x00))};
  100. constexpr int kSizeLabelBorderRadius = 4;
  101. constexpr int kSizeLabelHorizontalPadding = 8;
  102. // Values for the shadows of the capture region components.
  103. constexpr int kRegionAffordanceCircleShadow2Blur = 6;
  104. constexpr gfx::ShadowValue kRegionOutlineShadow(gfx::Vector2d(0, 0),
  105. 2,
  106. SkColorSetARGB(41, 0, 0, 0));
  107. constexpr gfx::ShadowValue kRegionAffordanceCircleShadow1(
  108. gfx::Vector2d(0, 1),
  109. 2,
  110. SkColorSetARGB(76, 0, 0, 0));
  111. constexpr gfx::ShadowValue kRegionAffordanceCircleShadow2(
  112. gfx::Vector2d(0, 2),
  113. kRegionAffordanceCircleShadow2Blur,
  114. SkColorSetARGB(38, 0, 0, 0));
  115. // Values of the focus ring draw around the region or affordance circles.
  116. constexpr int kFocusRingStrokeWidthDp = 2;
  117. constexpr int kFocusRingSpacingDp = 2;
  118. // When updating the capture region, request a repaint on the region and inset
  119. // such that the border, affordance circles and affordance circle shadows are
  120. // all repainted as well.
  121. constexpr int kDamageInsetDp = capture_mode::kCaptureRegionBorderStrokePx +
  122. kAffordanceCircleRadiusDp +
  123. kRegionAffordanceCircleShadow2Blur;
  124. // The minimum padding on each side of the capture region. If the capture button
  125. // cannot be placed in the center of the capture region and maintain this
  126. // padding, it will be placed below or above the capture region.
  127. constexpr int kCaptureRegionMinimumPaddingDp = 16;
  128. // Animation parameters needed when countdown starts.
  129. // The animation duration that the label fades out and scales down before count
  130. // down starts.
  131. constexpr base::TimeDelta kCaptureLabelCountdownStartDuration =
  132. base::Milliseconds(267);
  133. // The animation duration that the capture widgets (capture bar, capture
  134. // settings) fade out before count down starts.
  135. constexpr base::TimeDelta kCaptureWidgetFadeOutDuration =
  136. base::Milliseconds(167);
  137. // The animation duration that the fullscreen shield fades out before count down
  138. // starts.
  139. constexpr base::TimeDelta kCaptureShieldFadeOutDuration =
  140. base::Milliseconds(333);
  141. // If there is no text message was showing when count down starts, the label
  142. // widget will shrink down from 120% -> 100% and fade in.
  143. constexpr float kLabelScaleUpOnCountdown = 1.2;
  144. // The animation duration that the label fades out and scales up when going from
  145. // the selection phase to the fine tune phase.
  146. constexpr base::TimeDelta kCaptureLabelRegionPhaseChangeDuration =
  147. base::Milliseconds(167);
  148. // The delay before the label fades out and scales up.
  149. constexpr base::TimeDelta kCaptureLabelRegionPhaseChangeDelay =
  150. base::Milliseconds(67);
  151. // When going from the select region phase to the fine tune phase, the label
  152. // widget will scale up from 80% -> 100%.
  153. constexpr float kLabelScaleDownOnPhaseChange = 0.8;
  154. // Animation parameters for capture UI (capture bar, capture label) overlapping
  155. // the user capture region or camera preview. The default animation duration for
  156. // opacity changes to the capture UI.
  157. constexpr base::TimeDelta kCaptureUIOpacityChangeDuration =
  158. base::Milliseconds(100);
  159. // If the user is using keyboard only and they are on the selecting region
  160. // phase, they can create default region which is centered and sized to this
  161. // value times the root window's width and height.
  162. constexpr float kRegionDefaultRatio = 0.24f;
  163. // Mouse cursor warping is disabled when the capture source is a custom region.
  164. // Sets the mouse warp status to |enable| and return the original value.
  165. bool SetMouseWarpEnabled(bool enable) {
  166. auto* mouse_cursor_filter = Shell::Get()->mouse_cursor_filter();
  167. const bool old_value = mouse_cursor_filter->mouse_warp_enabled();
  168. mouse_cursor_filter->set_mouse_warp_enabled(enable);
  169. return old_value;
  170. }
  171. // Gets the menu container inside |root|.
  172. aura::Window* GetParentContainer(aura::Window* root) {
  173. DCHECK(root);
  174. DCHECK(root->IsRootWindow());
  175. return root->GetChildById(kShellWindowId_MenuContainer);
  176. }
  177. // Returns the smallest rect that contains all of |points|.
  178. gfx::Rect GetRectEnclosingPoints(const std::vector<gfx::Point>& points) {
  179. DCHECK_GE(points.size(), 2u);
  180. int x = INT_MAX;
  181. int y = INT_MAX;
  182. int right = INT_MIN;
  183. int bottom = INT_MIN;
  184. for (const gfx::Point& point : points) {
  185. x = std::min(point.x(), x);
  186. y = std::min(point.y(), y);
  187. right = std::max(point.x(), right);
  188. bottom = std::max(point.y(), bottom);
  189. }
  190. return gfx::Rect(x, y, right - x, bottom - y);
  191. }
  192. // Returns the widget init params needed to create a widget associated with a
  193. // capture session.
  194. views::Widget::InitParams CreateWidgetParams(aura::Window* parent,
  195. const gfx::Rect& bounds,
  196. const std::string& name) {
  197. // Use a popup widget to get transient properties, such as not needing to
  198. // click on the widget first to get capture before receiving events.
  199. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  200. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  201. params.parent = parent;
  202. params.bounds = bounds;
  203. params.name = name;
  204. return params;
  205. }
  206. // Gets the root window associated |location_in_screen| if given, otherwise gets
  207. // the root window associated with the CursorManager.
  208. aura::Window* GetPreferredRootWindow(
  209. absl::optional<gfx::Point> location_in_screen = absl::nullopt) {
  210. int64_t display_id =
  211. (location_in_screen
  212. ? display::Screen::GetScreen()->GetDisplayNearestPoint(
  213. *location_in_screen)
  214. : Shell::Get()->cursor_manager()->GetDisplay())
  215. .id();
  216. // The Display object returned by CursorManager::GetDisplay may be stale, but
  217. // will have the correct id.
  218. DCHECK_NE(display::kInvalidDisplayId, display_id);
  219. return Shell::GetRootWindowForDisplayId(display_id);
  220. }
  221. // In fullscreen or window capture mode, the mouse will change to a camera
  222. // image icon if we're capturing image, or a video record image icon if we're
  223. // capturing video.
  224. ui::Cursor GetCursorForFullscreenOrWindowCapture(bool capture_image) {
  225. ui::Cursor cursor(ui::mojom::CursorType::kCustom);
  226. const display::Display display =
  227. display::Screen::GetScreen()->GetDisplayNearestWindow(
  228. GetPreferredRootWindow());
  229. const float device_scale_factor = display.device_scale_factor();
  230. const gfx::ImageSkia* icon =
  231. ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
  232. capture_image ? IDR_CAPTURE_IMAGE_CURSOR : IDR_CAPTURE_VIDEO_CURSOR);
  233. SkBitmap bitmap = *icon->bitmap();
  234. gfx::Point hotspot(bitmap.width() / 2, bitmap.height() / 2);
  235. wm::ScaleAndRotateCursorBitmapAndHotpoint(
  236. device_scale_factor, display.panel_rotation(), &bitmap, &hotspot);
  237. auto* cursor_factory = ui::CursorFactory::GetInstance();
  238. cursor.SetPlatformCursor(
  239. cursor_factory->CreateImageCursor(cursor.type(), bitmap, hotspot));
  240. cursor.set_custom_bitmap(bitmap);
  241. cursor.set_custom_hotspot(hotspot);
  242. return cursor;
  243. }
  244. // Returns the expected cursor type for |position| in region capture.
  245. ui::mojom::CursorType GetCursorTypeForFineTunePosition(
  246. FineTunePosition position) {
  247. switch (position) {
  248. case FineTunePosition::kTopLeft:
  249. return ui::mojom::CursorType::kNorthWestResize;
  250. case FineTunePosition::kBottomRight:
  251. return ui::mojom::CursorType::kSouthEastResize;
  252. case FineTunePosition::kTopCenter:
  253. case FineTunePosition::kBottomCenter:
  254. return ui::mojom::CursorType::kNorthSouthResize;
  255. case FineTunePosition::kTopRight:
  256. return ui::mojom::CursorType::kNorthEastResize;
  257. case FineTunePosition::kBottomLeft:
  258. return ui::mojom::CursorType::kSouthWestResize;
  259. case FineTunePosition::kLeftCenter:
  260. case FineTunePosition::kRightCenter:
  261. return ui::mojom::CursorType::kEastWestResize;
  262. case FineTunePosition::kCenter:
  263. return ui::mojom::CursorType::kMove;
  264. default:
  265. return ui::mojom::CursorType::kCell;
  266. }
  267. }
  268. // Gets the amount of change that should happen to a region given |event_flags|.
  269. int GetArrowKeyPressChange(int event_flags) {
  270. if ((event_flags & ui::EF_SHIFT_DOWN) != 0)
  271. return capture_mode::kShiftArrowKeyboardRegionChangeDp;
  272. if ((event_flags & ui::EF_CONTROL_DOWN) != 0)
  273. return capture_mode::kCtrlArrowKeyboardRegionChangeDp;
  274. return capture_mode::kArrowKeyboardRegionChangeDp;
  275. }
  276. // Clips |out_bounds| to fit |rect|. Similar to
  277. // gfx::Rect::AdjustToFit() but does not shift the output rect to maintain the
  278. // rect size.
  279. void ClipRectToFit(gfx::Rect* out_bounds, const gfx::Rect& rect) {
  280. out_bounds->SetByBounds(std::max(rect.x(), out_bounds->x()),
  281. std::max(rect.y(), out_bounds->y()),
  282. std::min(rect.right(), out_bounds->right()),
  283. std::min(rect.bottom(), out_bounds->bottom()));
  284. }
  285. // Returns the `message_id` for the chromevox alert when capture session starts.
  286. int GetMessageIdForInitialCaptureSource(CaptureModeSource source) {
  287. switch (source) {
  288. case CaptureModeSource::kFullscreen:
  289. return IDS_ASH_SCREEN_CAPTURE_SOURCE_FULLSCREEN;
  290. case CaptureModeSource::kRegion:
  291. return IDS_ASH_SCREEN_CAPTURE_SOURCE_PARTIAL;
  292. default:
  293. return IDS_ASH_SCREEN_CAPTURE_SOURCE_WINDOW;
  294. }
  295. }
  296. void UpdateFloatingPanelBoundsIfNeeded() {
  297. Shell::Get()->accessibility_controller()->UpdateFloatingPanelBoundsIfNeeded();
  298. }
  299. views::Widget* GetCameraPreviewWidget() {
  300. return CaptureModeController::Get()
  301. ->camera_controller()
  302. ->camera_preview_widget();
  303. }
  304. bool ShouldPassEventToCameraPreview(ui::LocatedEvent* event) {
  305. auto* controller = CaptureModeController::Get();
  306. // If there's a video recording in progress, return false immediately, since
  307. // even camera preview exists, it doesn't belong to the current capture
  308. // session.
  309. if (controller->is_recording_in_progress())
  310. return false;
  311. auto* camera_preview_widget = GetCameraPreviewWidget();
  312. if (!camera_preview_widget || !camera_preview_widget->IsVisible())
  313. return false;
  314. if (controller->camera_controller()->is_drag_in_progress())
  315. return true;
  316. // If the event is targeted on the camera preview, even it's not located
  317. // on the camera preview, we should still pass the event to camera preview
  318. // to handle it. For example, when pressing on the resize button inside camera
  319. // preview, but release the press outside of camera preview, even the release
  320. // event is not on the camera preview, we should still pass the event to it,
  321. // otherwise camera preview will wait for the release event forever which will
  322. // make the regular drag for camera preview not work.
  323. auto* target = static_cast<aura::Window*>(event->target());
  324. if (camera_preview_widget->GetNativeWindow()->Contains(target))
  325. return true;
  326. return false;
  327. }
  328. // Returns true if the given `widget` intersects with the camera preview.
  329. // Otherwise, returns false;
  330. bool IsWidgetOverlappedWithCameraPreview(views::Widget* widget) {
  331. // Return false immediately if there's a video recording in propress since
  332. // the camera preview doesn't belong to the current capture session.
  333. if (CaptureModeController::Get()->is_recording_in_progress())
  334. return false;
  335. auto* camera_preview_widget = GetCameraPreviewWidget();
  336. if (!camera_preview_widget)
  337. return false;
  338. return camera_preview_widget->IsVisible() &&
  339. camera_preview_widget->GetLayer()->GetTargetOpacity() > 0.f &&
  340. camera_preview_widget->GetWindowBoundsInScreen().Intersects(
  341. widget->GetWindowBoundsInScreen());
  342. }
  343. } // namespace
  344. // -----------------------------------------------------------------------------
  345. // CaptureModeSession::CursorSetter:
  346. class CaptureModeSession::CursorSetter {
  347. public:
  348. CursorSetter()
  349. : cursor_manager_(Shell::Get()->cursor_manager()),
  350. original_cursor_(cursor_manager_->GetCursor()),
  351. original_cursor_visible_(cursor_manager_->IsCursorVisible()),
  352. original_cursor_locked_(cursor_manager_->IsCursorLocked()),
  353. current_orientation_(GetCurrentScreenOrientation()) {
  354. if (!cursor_manager_->IsMouseEventsEnabled())
  355. cursor_manager_->EnableMouseEvents();
  356. }
  357. CursorSetter(const CursorSetter&) = delete;
  358. CursorSetter& operator=(const CursorSetter&) = delete;
  359. ~CursorSetter() { ResetCursor(); }
  360. // Note that this will always make the cursor visible if it is not |kNone|.
  361. void UpdateCursor(const ui::Cursor& cursor) {
  362. if (original_cursor_locked_)
  363. return;
  364. if (in_cursor_update_)
  365. return;
  366. base::AutoReset<bool> auto_reset_in_cursor_update(&in_cursor_update_, true);
  367. const ui::mojom::CursorType current_cursor_type =
  368. cursor_manager_->GetCursor().type();
  369. const ui::mojom::CursorType new_cursor_type = cursor.type();
  370. const CaptureModeType capture_type = CaptureModeController::Get()->type();
  371. const float device_scale_factor =
  372. display::Screen::GetScreen()
  373. ->GetDisplayNearestWindow(GetPreferredRootWindow())
  374. .device_scale_factor();
  375. // For custom cursors, update the cursor if we need to change between image
  376. // capture and video capture, if the device scale factor changes, or if the
  377. // screen orientation changes.
  378. const chromeos::OrientationType orientation = GetCurrentScreenOrientation();
  379. const bool is_cursor_changed =
  380. current_cursor_type != new_cursor_type ||
  381. (current_cursor_type == ui::mojom::CursorType::kCustom &&
  382. (custom_cursor_capture_type_ != capture_type ||
  383. custom_cursor_device_scale_factor_ != device_scale_factor ||
  384. current_orientation_ != orientation));
  385. const bool is_cursor_visibility_changed =
  386. cursor_manager_->IsCursorVisible() !=
  387. (new_cursor_type != ui::mojom::CursorType::kNone);
  388. if (new_cursor_type == ui::mojom::CursorType::kCustom) {
  389. custom_cursor_capture_type_ = capture_type;
  390. custom_cursor_device_scale_factor_ = device_scale_factor;
  391. }
  392. current_orientation_ = orientation;
  393. if (!is_cursor_changed && !is_cursor_visibility_changed)
  394. return;
  395. if (cursor_manager_->IsCursorLocked())
  396. cursor_manager_->UnlockCursor();
  397. if (new_cursor_type == ui::mojom::CursorType::kNone) {
  398. cursor_manager_->HideCursor();
  399. } else {
  400. cursor_manager_->SetCursor(cursor);
  401. cursor_manager_->ShowCursor();
  402. }
  403. cursor_manager_->LockCursor();
  404. was_cursor_reset_to_original_ = false;
  405. }
  406. // Resets to its original cursor.
  407. void ResetCursor() {
  408. // Only unlock the cursor if it wasn't locked before.
  409. if (original_cursor_locked_)
  410. return;
  411. // Only reset cursor if it hasn't been reset before.
  412. if (was_cursor_reset_to_original_)
  413. return;
  414. if (cursor_manager_->IsCursorLocked())
  415. cursor_manager_->UnlockCursor();
  416. cursor_manager_->SetCursor(original_cursor_);
  417. if (original_cursor_visible_)
  418. cursor_manager_->ShowCursor();
  419. else
  420. cursor_manager_->HideCursor();
  421. was_cursor_reset_to_original_ = true;
  422. }
  423. bool IsCursorVisible() const { return cursor_manager_->IsCursorVisible(); }
  424. void HideCursor() {
  425. if (original_cursor_locked_ || !IsCursorVisible())
  426. return;
  427. if (cursor_manager_->IsCursorLocked())
  428. cursor_manager_->UnlockCursor();
  429. cursor_manager_->HideCursor();
  430. cursor_manager_->LockCursor();
  431. was_cursor_reset_to_original_ = false;
  432. }
  433. bool IsUsingCustomCursor(CaptureModeType type) const {
  434. return cursor_manager_->GetCursor().type() ==
  435. ui::mojom::CursorType::kCustom &&
  436. custom_cursor_capture_type_ == type;
  437. }
  438. private:
  439. wm::CursorManager* const cursor_manager_;
  440. const gfx::NativeCursor original_cursor_;
  441. const bool original_cursor_visible_;
  442. // If the original cursor is already locked, don't make any changes to it.
  443. const bool original_cursor_locked_;
  444. // The current custom cursor type. kImage if we're using image capture icon as
  445. // the mouse cursor, and kVideo if we're using video record icon as the mouse
  446. // cursor.
  447. CaptureModeType custom_cursor_capture_type_ = CaptureModeType::kImage;
  448. // Records the current device scale factor. If the DSF changes, we will need
  449. // to update the cursor if we're using a custom cursor.
  450. float custom_cursor_device_scale_factor_ = 1.f;
  451. // Records the current screen orientation. If screen orientation changes, we
  452. // will need to update the cursor if we're using custom cursor.
  453. chromeos::OrientationType current_orientation_;
  454. // True if the cursor has reset back to its original cursor. It's to prevent
  455. // Reset() from setting the cursor to |original_cursor_| more than once.
  456. bool was_cursor_reset_to_original_ = true;
  457. // True if the cursor is currently being updated. This is to prevent
  458. // UpdateCursor() is called nestly more than once and the mouse is locked
  459. // multiple times.
  460. bool in_cursor_update_ = false;
  461. };
  462. // -----------------------------------------------------------------------------
  463. // CaptureModeSession::ParentContainerObserver:
  464. // The observer class to observer window added to or removed from the parent
  465. // container `kShellWindowId_MenuContainer`. Capture UIs (capture bar, capture
  466. // label, capture settings, camera preview) are all parented to the parent
  467. // container, thus whenever there's a window added or removed, we need to call
  468. // `RefreshStackingOrder` to ensure the stacking order is correct for them.
  469. class CaptureModeSession::ParentContainerObserver
  470. : public aura::WindowObserver {
  471. public:
  472. ParentContainerObserver(aura::Window* parent_container,
  473. CaptureModeSession* capture_mode_session)
  474. : parent_container_(parent_container),
  475. capture_mode_session_(capture_mode_session) {
  476. parent_container_->AddObserver(this);
  477. }
  478. ParentContainerObserver(const ParentContainerObserver&) = delete;
  479. ParentContainerObserver& operator=(const ParentContainerObserver&) = delete;
  480. ~ParentContainerObserver() override {
  481. if (parent_container_)
  482. parent_container_->RemoveObserver(this);
  483. }
  484. // aura::WindowObserver:
  485. void OnWindowAdded(aura::Window* window) override {
  486. capture_mode_session_->RefreshStackingOrder();
  487. capture_mode_session_->MaybeUpdateCaptureUisOpacity();
  488. }
  489. void OnWindowRemoved(aura::Window* window) override {
  490. capture_mode_session_->RefreshStackingOrder();
  491. capture_mode_session_->MaybeUpdateCaptureUisOpacity();
  492. }
  493. void OnWindowDestroying(aura::Window* window) override {
  494. parent_container_->RemoveObserver(this);
  495. parent_container_ = nullptr;
  496. }
  497. private:
  498. aura::Window* parent_container_;
  499. // Pointer to current capture session. Not nullptr during this lifecycle.
  500. // Capture session owns `this`.
  501. CaptureModeSession* const capture_mode_session_;
  502. };
  503. // -----------------------------------------------------------------------------
  504. // CaptureModeSession:
  505. CaptureModeSession::CaptureModeSession(CaptureModeController* controller,
  506. bool projector_mode)
  507. : controller_(controller),
  508. current_root_(GetPreferredRootWindow()),
  509. magnifier_glass_(kMagnifierParams),
  510. is_in_projector_mode_(projector_mode),
  511. cursor_setter_(std::make_unique<CursorSetter>()),
  512. focus_cycler_(std::make_unique<CaptureModeSessionFocusCycler>(this)),
  513. capture_toast_controller_(this) {}
  514. CaptureModeSession::~CaptureModeSession() = default;
  515. void CaptureModeSession::Initialize() {
  516. // Trigger this before creating `capture_mode_bar_widget_` as we want to read
  517. // out this message before reading out the first view of
  518. // `capture_mode_bar_widget_`.
  519. capture_mode_util::TriggerAccessibilityAlert(l10n_util::GetStringFUTF8(
  520. IDS_ASH_SCREEN_CAPTURE_ALERT_OPEN,
  521. l10n_util::GetStringUTF16(
  522. GetMessageIdForInitialCaptureSource(controller_->source())),
  523. l10n_util::GetStringUTF16(
  524. controller_->type() == CaptureModeType::kImage
  525. ? IDS_ASH_SCREEN_CAPTURE_TYPE_SCREENSHOT
  526. : IDS_ASH_SCREEN_CAPTURE_TYPE_SCREEN_RECORDING)));
  527. // A context menu may have input capture when entering a session. Remove
  528. // capture from it, otherwise subsequent mouse events will cause it to close,
  529. // and then we won't be able to take a screenshot of the menu. Store it so we
  530. // can return capture to it when exiting the session.
  531. // Note that some windows gets destroyed when they lose the capture (e.g. a
  532. // window created for capturing events while drag-drop in progress), so we
  533. // need to account for that.
  534. auto* capture_client = aura::client::GetCaptureClient(current_root_);
  535. input_capture_window_ = capture_client->GetCaptureWindow();
  536. if (input_capture_window_) {
  537. aura::WindowTracker tracker({input_capture_window_});
  538. capture_client->ReleaseCapture(input_capture_window_);
  539. if (tracker.windows().empty())
  540. input_capture_window_ = nullptr;
  541. else
  542. input_capture_window_->AddObserver(this);
  543. }
  544. SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
  545. layer()->SetFillsBoundsOpaquely(false);
  546. layer()->set_delegate(this);
  547. auto* parent = GetParentContainer(current_root_);
  548. parent_container_observer_ =
  549. std::make_unique<ParentContainerObserver>(parent, this);
  550. parent->layer()->Add(layer());
  551. layer()->SetBounds(parent->bounds());
  552. layer()->SetName("CaptureModeSession");
  553. // The last region selected could have been on a larger display. Ensure that
  554. // the region is not larger than the current display.
  555. ClampCaptureRegionToRootWindowSize();
  556. capture_mode_bar_widget_->Init(CreateWidgetParams(
  557. parent,
  558. CaptureModeBarView::GetBounds(current_root_, is_in_projector_mode_),
  559. "CaptureModeBarWidget"));
  560. capture_mode_bar_view_ = capture_mode_bar_widget_->SetContentsView(
  561. std::make_unique<CaptureModeBarView>(is_in_projector_mode_));
  562. capture_mode_bar_widget_->GetNativeWindow()->SetTitle(
  563. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_A11Y_TITLE));
  564. capture_mode_bar_widget_->Show();
  565. // Advance focus once if spoken feedback is on so that the capture bar takes
  566. // spoken feedback focus.
  567. if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled())
  568. focus_cycler_->AdvanceFocus(/*reverse=*/false);
  569. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  570. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  571. /*is_touch=*/false);
  572. if (controller_->source() == CaptureModeSource::kWindow)
  573. capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
  574. UpdateRootWindowDimmers();
  575. TabletModeController::Get()->AddObserver(this);
  576. display_observer_.emplace(this);
  577. // Our event handling code assumes the capture bar widget has been initialized
  578. // already. So we start handling events after everything has been setup.
  579. aura::Env::GetInstance()->AddPreTargetHandler(
  580. this, ui::EventTarget::Priority::kSystem);
  581. UpdateFloatingPanelBoundsIfNeeded();
  582. // call `OnCaptureTypeChanged` after capture bar's initialization is done
  583. // instead of in the initialization of the capture mode type view, since
  584. // `OnCaptureTypeChanged` may trigger `ShowCaptureToast` which depends on the
  585. // capture bar.
  586. // Also please note we should call `OnCaptureTypeChanged` in
  587. // `CaptureModeTypeView` instead of `CaptureModeSession`, since this is during
  588. // the initialization of the capture session, the type change is not triggered
  589. // by the user.
  590. capture_mode_bar_view_->capture_type_view()->OnCaptureTypeChanged(
  591. controller_->type());
  592. MaybeCreateUserNudge();
  593. if (is_in_projector_mode_)
  594. controller_->camera_controller()->MaybeSelectFirstCamera();
  595. Shell::Get()->AddShellObserver(this);
  596. }
  597. void CaptureModeSession::Shutdown() {
  598. is_shutting_down_ = true;
  599. aura::Env::GetInstance()->RemovePreTargetHandler(this);
  600. display_observer_.reset();
  601. user_nudge_controller_.reset();
  602. TabletModeController::Get()->RemoveObserver(this);
  603. if (input_capture_window_) {
  604. input_capture_window_->RemoveObserver(this);
  605. aura::client::GetCaptureClient(current_root_)
  606. ->SetCapture(input_capture_window_);
  607. }
  608. // This may happen if we hit esc while dragging.
  609. if (old_mouse_warp_status_)
  610. SetMouseWarpEnabled(*old_mouse_warp_status_);
  611. // Close all widgets immediately to avoid having them show up in the captured
  612. // screenshots or video.
  613. for (auto* widget : GetAvailableWidgets())
  614. widget->CloseNow();
  615. if (a11y_alert_on_session_exit_) {
  616. capture_mode_util::TriggerAccessibilityAlert(
  617. IDS_ASH_SCREEN_CAPTURE_ALERT_CLOSE);
  618. }
  619. UpdateFloatingPanelBoundsIfNeeded();
  620. if (!is_stopping_to_start_video_recording_) {
  621. // Stopping the session for any reason other than starting video recording
  622. // means a cancellation to an ongoing projector session (if any).
  623. if (is_in_projector_mode_)
  624. ProjectorControllerImpl::Get()->OnRecordingStartAborted();
  625. // Kill the camera preview when the capture mode session ends without
  626. // starting any recording.
  627. if (!controller_->is_recording_in_progress())
  628. controller_->camera_controller()->SetShouldShowPreview(false);
  629. }
  630. Shell::Get()->RemoveShellObserver(this);
  631. }
  632. aura::Window* CaptureModeSession::GetSelectedWindow() const {
  633. return capture_window_observer_ ? capture_window_observer_->window()
  634. : nullptr;
  635. }
  636. void CaptureModeSession::A11yAlertCaptureSource(bool trigger_now) {
  637. auto* controller = CaptureModeController::Get();
  638. const bool is_capturing_image = controller->type() == CaptureModeType::kImage;
  639. std::string message;
  640. switch (controller->source()) {
  641. case CaptureModeSource::kFullscreen:
  642. message = l10n_util::GetStringUTF8(
  643. is_capturing_image
  644. ? IDS_ASH_SCREEN_CAPTURE_ALERT_FULLSCREEN_SCREENSHOT
  645. : IDS_ASH_SCREEN_CAPTURE_ALERT_FULLSCREEN_RECORD);
  646. break;
  647. case CaptureModeSource::kRegion:
  648. if (!controller->user_capture_region().IsEmpty()) {
  649. message = l10n_util::GetStringUTF8(
  650. is_capturing_image ? IDS_ASH_SCREEN_CAPTURE_ALERT_REGION_SCREENSHOT
  651. : IDS_ASH_SCREEN_CAPTURE_ALERT_REGION_RECORD);
  652. }
  653. break;
  654. case CaptureModeSource::kWindow:
  655. // Selected window could be non-empty when switching to capture type.
  656. if (GetSelectedWindow()) {
  657. message = l10n_util::GetStringUTF8(
  658. is_capturing_image ? IDS_ASH_SCREEN_CAPTURE_ALERT_WINDOW_SCREENSHOT
  659. : IDS_ASH_SCREEN_CAPTURE_ALERT_WINDOW_RECORD);
  660. }
  661. break;
  662. }
  663. if (!message.empty()) {
  664. if (trigger_now)
  665. capture_mode_util::TriggerAccessibilityAlert(message);
  666. else
  667. capture_mode_util::TriggerAccessibilityAlertSoon(message);
  668. }
  669. }
  670. void CaptureModeSession::A11yAlertCaptureType() {
  671. capture_mode_util::TriggerAccessibilityAlert(
  672. CaptureModeController::Get()->type() == CaptureModeType::kImage
  673. ? IDS_ASH_SCREEN_CAPTURE_ALERT_SELECT_TYPE_IMAGE
  674. : IDS_ASH_SCREEN_CAPTURE_ALERT_SELECT_TYPE_VIDEO);
  675. A11yAlertCaptureSource(/*trigger_now=*/false);
  676. }
  677. void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) {
  678. capture_source_changed_ = true;
  679. if (new_source == CaptureModeSource::kWindow)
  680. capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
  681. else
  682. capture_window_observer_.reset();
  683. if (new_source == CaptureModeSource::kRegion)
  684. num_capture_region_adjusted_ = 0;
  685. capture_mode_bar_view_->OnCaptureSourceChanged(new_source);
  686. UpdateDimensionsLabelWidget(/*is_resizing=*/false);
  687. layer()->SchedulePaint(layer()->bounds());
  688. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  689. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  690. /*is_touch=*/false);
  691. if (focus_cycler_->RegionGroupFocused())
  692. focus_cycler_->ClearFocus();
  693. A11yAlertCaptureSource(/*trigger_now=*/true);
  694. MaybeReparentCameraPreviewWidget();
  695. }
  696. void CaptureModeSession::OnCaptureTypeChanged(CaptureModeType new_type) {
  697. capture_mode_bar_view_->OnCaptureTypeChanged(new_type);
  698. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  699. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  700. /*is_touch=*/false);
  701. A11yAlertCaptureType();
  702. }
  703. void CaptureModeSession::OnWaitingForDlpConfirmationStarted() {
  704. is_waiting_for_dlp_confirmation_ = true;
  705. HideAllUis();
  706. }
  707. void CaptureModeSession::OnWaitingForDlpConfirmationEnded(bool reshow_uis) {
  708. is_waiting_for_dlp_confirmation_ = false;
  709. if (reshow_uis)
  710. ShowAllUis();
  711. }
  712. void CaptureModeSession::SetSettingsMenuShown(bool shown) {
  713. capture_mode_bar_view_->SetSettingsMenuShown(shown);
  714. if (!shown) {
  715. capture_mode_settings_widget_.reset();
  716. capture_mode_settings_view_ = nullptr;
  717. // After closing CaptureMode settings view, show CaptureLabel view if it has
  718. // been hidden.
  719. if (capture_label_widget_ && !capture_label_widget_->IsVisible())
  720. capture_label_widget_->Show();
  721. return;
  722. }
  723. if (!capture_mode_settings_widget_) {
  724. auto* parent = GetParentContainer(current_root_);
  725. capture_mode_settings_widget_ = std::make_unique<views::Widget>();
  726. MaybeDismissUserNudgeForever();
  727. capture_toast_controller_.DismissCurrentToastIfAny();
  728. capture_mode_settings_widget_->Init(CreateWidgetParams(
  729. parent, CaptureModeSettingsView::GetBounds(capture_mode_bar_view_),
  730. "CaptureModeSettingsWidget"));
  731. capture_mode_settings_view_ =
  732. capture_mode_settings_widget_->SetContentsView(
  733. std::make_unique<CaptureModeSettingsView>(this,
  734. is_in_projector_mode_));
  735. OnCaptureFolderMayHaveChanged();
  736. parent->layer()->StackAtTop(capture_mode_settings_widget_->GetLayer());
  737. focus_cycler_->OnSettingsMenuWidgetCreated();
  738. if (capture_label_widget_ && capture_label_widget_->IsVisible()) {
  739. // Hide CaptureLabel view if it overlaps with CaptureMode settings view.
  740. if (capture_mode_settings_widget_->GetWindowBoundsInScreen().Intersects(
  741. capture_label_widget_->GetWindowBoundsInScreen())) {
  742. capture_label_widget_->Hide();
  743. }
  744. }
  745. capture_mode_settings_widget_->GetNativeWindow()->SetTitle(
  746. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_SETTINGS_A11Y_TITLE));
  747. capture_mode_settings_widget_->Show();
  748. }
  749. }
  750. void CaptureModeSession::ReportSessionHistograms() {
  751. if (controller_->source() == CaptureModeSource::kRegion) {
  752. RecordNumberOfCaptureRegionAdjustments(num_capture_region_adjusted_,
  753. is_in_projector_mode_);
  754. }
  755. num_capture_region_adjusted_ = 0;
  756. RecordCaptureModeSwitchesFromInitialMode(capture_source_changed_);
  757. RecordCaptureModeConfiguration(controller_->type(), controller_->source(),
  758. controller_->enable_audio_recording(),
  759. is_in_projector_mode_);
  760. }
  761. void CaptureModeSession::StartCountDown(
  762. base::OnceClosure countdown_finished_callback) {
  763. DCHECK(capture_label_widget_);
  764. // Show CaptureLabel view if it has been hidden. Since `capture_label_widget_`
  765. // is the only widget which should be shown on 3-seconds count down starts,
  766. // there's no need to consider if it insects with
  767. // `capture_mode_settings_widget_` or not here.
  768. if (!capture_label_widget_->IsVisible())
  769. capture_label_widget_->Show();
  770. CaptureLabelView* label_view =
  771. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
  772. label_view->StartCountDown(std::move(countdown_finished_callback));
  773. UpdateCaptureLabelWidgetBounds(CaptureLabelAnimation::kCountdownStart);
  774. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  775. /*is_touch=*/false);
  776. // Fade out the capture bar, capture settings and capture toast if they exist.
  777. std::vector<ui::Layer*> layers_to_fade_out{
  778. capture_mode_bar_widget_->GetLayer()};
  779. if (capture_mode_settings_widget_)
  780. layers_to_fade_out.push_back(capture_mode_settings_widget_->GetLayer());
  781. if (auto* toast_layer = capture_toast_controller_.MaybeGetToastLayer())
  782. layers_to_fade_out.push_back(toast_layer);
  783. for (auto* layer : layers_to_fade_out) {
  784. ui::ScopedLayerAnimationSettings layer_settings(layer->GetAnimator());
  785. layer_settings.SetTransitionDuration(kCaptureWidgetFadeOutDuration);
  786. layer_settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  787. layer_settings.SetPreemptionStrategy(
  788. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  789. layer->SetOpacity(0.f);
  790. }
  791. // Do a repaint to hide the affordance circles.
  792. RepaintRegion();
  793. // Fade out the shield if it's recording fullscreen.
  794. if (controller_->source() == CaptureModeSource::kFullscreen) {
  795. ui::ScopedLayerAnimationSettings shield_settings(layer()->GetAnimator());
  796. shield_settings.SetTransitionDuration(kCaptureShieldFadeOutDuration);
  797. shield_settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  798. shield_settings.SetPreemptionStrategy(
  799. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  800. layer()->SetOpacity(0.f);
  801. }
  802. }
  803. void CaptureModeSession::OpenFolderSelectionDialog() {
  804. DCHECK(!folder_selection_dialog_controller_);
  805. folder_selection_dialog_controller_ =
  806. std::make_unique<FolderSelectionDialogController>(/*delegate=*/this,
  807. current_root_);
  808. // We have to call `MaybeUpdateCameraPreviewBounds()` here after
  809. // `folder_selection_dialog_controller_` has been set, since
  810. // `CalculateCameraPreviewTargetVisibility()` checks its existence to
  811. // determine the target visibility of the camera preview. We cannot however
  812. // call it in `OnSelectionWindowAdded()` since this function can be called
  813. // inside the constructor of `FolderSelectionDialogController` before the
  814. // `folder_selection_dialog_controller_` member is set above.
  815. MaybeUpdateCameraPreviewBounds();
  816. }
  817. bool CaptureModeSession::IsInCountDownAnimation() const {
  818. if (is_shutting_down_)
  819. return false;
  820. CaptureLabelView* label_view =
  821. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
  822. return label_view->IsInCountDownAnimation();
  823. }
  824. void CaptureModeSession::OnCaptureFolderMayHaveChanged() {
  825. if (!capture_mode_settings_widget_)
  826. return;
  827. DCHECK(capture_mode_settings_view_);
  828. capture_mode_settings_view_->OnCaptureFolderMayHaveChanged();
  829. MaybeUpdateSettingsBounds();
  830. }
  831. void CaptureModeSession::OnDefaultCaptureFolderSelectionChanged() {
  832. if (!capture_mode_settings_widget_)
  833. return;
  834. DCHECK(capture_mode_settings_view_);
  835. capture_mode_settings_view_->OnDefaultCaptureFolderSelectionChanged();
  836. }
  837. aura::Window* CaptureModeSession::GetCameraPreviewParentWindow() const {
  838. auto* controller = CaptureModeController::Get();
  839. DCHECK(!controller->is_recording_in_progress());
  840. auto* menu_container =
  841. current_root_->GetChildById(kShellWindowId_MenuContainer);
  842. auto* unparented_container =
  843. current_root_->GetChildById(kShellWindowId_UnparentedContainer);
  844. switch (controller->source()) {
  845. case CaptureModeSource::kFullscreen:
  846. return menu_container;
  847. case CaptureModeSource::kRegion:
  848. return controller_->user_capture_region().IsEmpty() ? unparented_container
  849. : menu_container;
  850. case CaptureModeSource::kWindow:
  851. aura::Window* selected_window = GetSelectedWindow();
  852. return selected_window ? selected_window : unparented_container;
  853. }
  854. }
  855. gfx::Rect CaptureModeSession::GetCameraPreviewConfineBounds() const {
  856. auto* controller = CaptureModeController::Get();
  857. DCHECK(!controller->is_recording_in_progress());
  858. switch (controller->source()) {
  859. case CaptureModeSource::kFullscreen: {
  860. auto* parent = GetCameraPreviewParentWindow();
  861. DCHECK(parent);
  862. return display::Screen::GetScreen()
  863. ->GetDisplayNearestWindow(parent)
  864. .work_area();
  865. }
  866. case CaptureModeSource::kWindow: {
  867. aura::Window* selected_window = GetSelectedWindow();
  868. return selected_window ? gfx::Rect(selected_window->bounds().size())
  869. : gfx::Rect();
  870. }
  871. case CaptureModeSource::kRegion: {
  872. gfx::Rect capture_region = controller->user_capture_region();
  873. wm::ConvertRectToScreen(current_root_, &capture_region);
  874. return capture_region;
  875. }
  876. }
  877. }
  878. bool CaptureModeSession::CalculateCameraPreviewTargetVisibility() const {
  879. // A screenshot-only-session opened during recording should not affect the
  880. // visibility of a camera preview created for that on-going recording.
  881. if (controller_->is_recording_in_progress())
  882. return true;
  883. // The camera preview should be hidden while the folder selection dialog is
  884. // shown in order to avoid it being on top of the dialog and blocking
  885. // interacting with it. This is consistent with what we do with the rest of
  886. // the capture mode UIs (see OnSelectionWindowAdded()).
  887. if (folder_selection_dialog_controller_)
  888. return false;
  889. // For fullscreen and window sources, the visibility of the camera preview is
  890. // determined by the preview's size specs, and whether there's a window source
  891. // selected. We only care about region sources here, since the visibility of
  892. // the preview in this case depends on what's happening to the region during
  893. // this session.
  894. if (controller_->source() != CaptureModeSource::kRegion)
  895. return true;
  896. if (controller_->user_capture_region().IsEmpty())
  897. return false;
  898. // Adjusting the region from any of its affordance points should result in
  899. // hiding the preview. However, dragging it around from its center should not
  900. // change the visibility.
  901. return !is_drag_in_progress_ ||
  902. fine_tune_position_ == FineTunePosition::kCenter;
  903. }
  904. void CaptureModeSession::OnPaintLayer(const ui::PaintContext& context) {
  905. // If the drag of camera preview is in progress, we will hide other capture
  906. // UIs (capture bar, capture label), but we should still paint the layer to
  907. // indicate the capture surface where user can drag camera preview on.
  908. if (!is_all_uis_visible_ &&
  909. !controller_->camera_controller()->is_drag_in_progress()) {
  910. return;
  911. }
  912. ui::PaintRecorder recorder(context, layer()->size());
  913. recorder.canvas()->DrawColor(capture_mode::kDimmingShieldColor);
  914. PaintCaptureRegion(recorder.canvas());
  915. }
  916. void CaptureModeSession::OnKeyEvent(ui::KeyEvent* event) {
  917. // We don't consume any events while a DLP system-modal dialog might be shown,
  918. // so that the user may interact with it.
  919. if (is_waiting_for_dlp_confirmation_)
  920. return;
  921. if (folder_selection_dialog_controller_) {
  922. if (folder_selection_dialog_controller_->ShouldConsumeEvent(event))
  923. event->StopPropagation();
  924. return;
  925. }
  926. if (event->type() != ui::ET_KEY_PRESSED)
  927. return;
  928. auto* camera_preview_view =
  929. controller_->camera_controller()->camera_preview_view();
  930. if (camera_preview_view && camera_preview_view->MaybeHandleKeyEvent(event)) {
  931. event->StopPropagation();
  932. return;
  933. }
  934. // We create an owned heap-allocated boolean to pass it to `deferred_runner`
  935. // and hold a pointer to the boolean to be able to change its value to control
  936. // whether the deferred runner should call `MaybeUpdateCaptureUisOpacity` or
  937. // not.
  938. auto should_update_opacity = std::make_unique<bool>(false);
  939. auto* should_update_opacity_ptr = should_update_opacity.get();
  940. // Run at the exit of this function to update opacity of capture UIs when
  941. // necessary.
  942. base::ScopedClosureRunner deferred_runner(base::BindOnce(
  943. [](base::WeakPtr<CaptureModeSession> session,
  944. std::unique_ptr<bool> should_update_opacity) {
  945. if (*should_update_opacity && session)
  946. session->MaybeUpdateCaptureUisOpacity();
  947. },
  948. weak_ptr_factory_.GetWeakPtr(), std::move(should_update_opacity)));
  949. ui::KeyboardCode key_code = event->key_code();
  950. switch (key_code) {
  951. case ui::VKEY_ESCAPE: {
  952. event->StopPropagation();
  953. *should_update_opacity_ptr = true;
  954. // We only dismiss the settings menu or clear the focus on ESC key if the
  955. // count down is not in progress.
  956. const bool is_in_count_down = IsInCountDownAnimation();
  957. if (capture_mode_settings_widget_ && !is_in_count_down)
  958. SetSettingsMenuShown(false);
  959. else if (focus_cycler_->HasFocus() && !is_in_count_down)
  960. focus_cycler_->ClearFocus();
  961. else if (can_exit_on_escape_)
  962. controller_->Stop(); // `this` is destroyed here.
  963. return;
  964. }
  965. case ui::VKEY_RETURN: {
  966. event->StopPropagation();
  967. if (!IsInCountDownAnimation())
  968. DoPerformCapture(); // `this` can be deleted after this.
  969. return;
  970. }
  971. case ui::VKEY_SPACE: {
  972. event->StopPropagation();
  973. event->SetHandled();
  974. if (focus_cycler_->OnSpacePressed()) {
  975. *should_update_opacity_ptr = true;
  976. return;
  977. }
  978. // Create a default region if we are in region mode and there is no
  979. // existing region.
  980. if (controller_->source() == CaptureModeSource::kRegion &&
  981. controller_->user_capture_region().IsEmpty()) {
  982. SelectDefaultRegion();
  983. }
  984. return;
  985. }
  986. case ui::VKEY_TAB: {
  987. event->StopPropagation();
  988. event->SetHandled();
  989. focus_cycler_->AdvanceFocus(/*reverse=*/event->IsShiftDown());
  990. *should_update_opacity_ptr = true;
  991. return;
  992. }
  993. case ui::VKEY_UP:
  994. case ui::VKEY_DOWN: {
  995. event->StopPropagation();
  996. event->SetHandled();
  997. UpdateRegionVertically(/*up=*/key_code == ui::VKEY_UP, event->flags());
  998. return;
  999. }
  1000. case ui::VKEY_LEFT:
  1001. case ui::VKEY_RIGHT: {
  1002. event->StopPropagation();
  1003. event->SetHandled();
  1004. UpdateRegionHorizontally(/*left=*/key_code == ui::VKEY_LEFT,
  1005. event->flags());
  1006. return;
  1007. }
  1008. default:
  1009. return;
  1010. }
  1011. }
  1012. void CaptureModeSession::OnMouseEvent(ui::MouseEvent* event) {
  1013. // We don't consume any events while a DLP system-modal dialog might be shown,
  1014. // so that the user may interact with it.
  1015. if (is_waiting_for_dlp_confirmation_)
  1016. return;
  1017. OnLocatedEvent(event, /*is_touch=*/false);
  1018. }
  1019. void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) {
  1020. // We don't consume any events while a DLP system-modal dialog might be shown,
  1021. // so that the user may interact with it.
  1022. if (is_waiting_for_dlp_confirmation_)
  1023. return;
  1024. OnLocatedEvent(event, /*is_touch=*/true);
  1025. }
  1026. void CaptureModeSession::OnTabletModeStarted() {
  1027. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  1028. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  1029. /*is_touch=*/false);
  1030. }
  1031. void CaptureModeSession::OnTabletModeEnded() {
  1032. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  1033. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  1034. /*is_touch=*/false);
  1035. }
  1036. void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
  1037. DCHECK_EQ(window, input_capture_window_);
  1038. input_capture_window_->RemoveObserver(this);
  1039. input_capture_window_ = nullptr;
  1040. }
  1041. void CaptureModeSession::OnRootWindowWillShutdown(aura::Window* root_window) {
  1042. if (root_window == current_root_) {
  1043. // There should always be a primary root window.
  1044. DCHECK_NE(Shell::GetPrimaryRootWindow(), current_root_);
  1045. MaybeChangeRoot(Shell::GetPrimaryRootWindow());
  1046. }
  1047. }
  1048. void CaptureModeSession::OnDisplayMetricsChanged(
  1049. const display::Display& display,
  1050. uint32_t metrics) {
  1051. if (!(metrics &
  1052. (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION |
  1053. DISPLAY_METRIC_DEVICE_SCALE_FACTOR | DISPLAY_METRIC_WORK_AREA))) {
  1054. return;
  1055. }
  1056. EndSelection();
  1057. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  1058. /*is_touch=*/false);
  1059. // Ensure the region still fits the root window after display changes.
  1060. ClampCaptureRegionToRootWindowSize();
  1061. // Update the bounds of all created widgets and repaint the entire layer.
  1062. auto* parent = GetParentContainer(current_root_);
  1063. DCHECK_EQ(parent->layer(), layer()->parent());
  1064. layer()->SetBounds(parent->bounds());
  1065. // We need to update the capture bar bounds first and then settings bounds.
  1066. // The sequence matters here since settings bounds depend on capture bar
  1067. // bounds.
  1068. RefreshBarWidgetBounds();
  1069. MaybeUpdateSettingsBounds();
  1070. // Only need to update the camera preview's bounds if the capture source is
  1071. // `kFullscreen`, since `ClampCaptureRegionToRootWindowSize` will take care of
  1072. // it if the source is `kRegion`.
  1073. // `CaptureWindowObserver::OnWindowBoundsChanged` will take care of it if the
  1074. // source is `kWindow`.
  1075. if (controller_->source() == CaptureModeSource::kFullscreen &&
  1076. !controller_->is_recording_in_progress()) {
  1077. controller_->camera_controller()->MaybeUpdatePreviewWidget();
  1078. }
  1079. if (capture_label_widget_)
  1080. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  1081. layer()->SchedulePaint(layer()->bounds());
  1082. MaybeUpdateCaptureUisOpacity();
  1083. }
  1084. void CaptureModeSession::OnFolderSelected(const base::FilePath& path) {
  1085. CaptureModeController::Get()->SetCustomCaptureFolder(path);
  1086. if (controller_->GetCurrentCaptureFolder().is_default_downloads_folder) {
  1087. RecordSwitchToDefaultFolderReason(
  1088. CaptureModeSwitchToDefaultReason::
  1089. kUserSelectedFromFolderSelectionDialog);
  1090. }
  1091. }
  1092. void CaptureModeSession::OnSelectionWindowAdded() {
  1093. // Hide all the capture session UIs so that they don't show on top of the
  1094. // selection dialog window and block it.
  1095. HideAllUis();
  1096. }
  1097. void CaptureModeSession::OnSelectionWindowClosed() {
  1098. DCHECK(folder_selection_dialog_controller_);
  1099. ShowAllUis();
  1100. const bool did_user_select_a_folder =
  1101. folder_selection_dialog_controller_->did_user_select_a_folder();
  1102. folder_selection_dialog_controller_.reset();
  1103. // This has to be called here after the `folder_selection_dialog_controller_`
  1104. // member was reset above, since `CalculateCameraPreviewTargetVisibility()`
  1105. // relies on its existence or lack thereof to determine the target visibility
  1106. // of the camera preview.
  1107. MaybeUpdateCameraPreviewBounds();
  1108. // If the selection window is closed by user selecting a folder, no need to
  1109. // update the capture folder settings menu here, since it's covered by
  1110. // `SetCustomCaptureFolder` via `OnFolderSelected`.
  1111. if (!did_user_select_a_folder)
  1112. OnCaptureFolderMayHaveChanged();
  1113. // Explicitly hide any virtual keyboard that may have remained open from
  1114. // interacting with the dialog selection window.
  1115. keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
  1116. }
  1117. void CaptureModeSession::UpdateCursor(const gfx::Point& location_in_screen,
  1118. bool is_touch) {
  1119. // No need to update cursor if `cursor_setter_` has been reset because of
  1120. // OpenFolderSelectionDialog.
  1121. if (is_shutting_down_ || !cursor_setter_)
  1122. return;
  1123. // Hide mouse cursor in tablet mode.
  1124. auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
  1125. if (tablet_mode_controller->InTabletMode() &&
  1126. !tablet_mode_controller->IsInDevTabletMode()) {
  1127. cursor_setter_->HideCursor();
  1128. return;
  1129. }
  1130. if (IsInCountDownAnimation()) {
  1131. cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
  1132. return;
  1133. }
  1134. // If the current located event should be handled by camera preview, use the
  1135. // pointer cursor.
  1136. if (should_pass_located_event_to_camera_preview_) {
  1137. cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
  1138. return;
  1139. }
  1140. // If the current mouse event is on capture label button, and capture label
  1141. // button can handle the event, show the hand mouse cursor.
  1142. const bool is_event_on_capture_button =
  1143. capture_label_widget_->GetWindowBoundsInScreen().Contains(
  1144. location_in_screen) &&
  1145. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView())
  1146. ->ShouldHandleEvent();
  1147. if (is_event_on_capture_button) {
  1148. cursor_setter_->UpdateCursor(ui::mojom::CursorType::kHand);
  1149. return;
  1150. }
  1151. // As long as the settings menu is open, a pointer cursor should be used as
  1152. // long as the cursor is not on top of the capture button, since clicking
  1153. // anywhere outside the bounds of either of them (the menu or the clickable
  1154. // capture button) will dismiss the menu. Also if the event is on the bar, a
  1155. // pointer will also be used, as long as the bar is visible.
  1156. const bool is_event_on_capture_bar =
  1157. capture_mode_bar_widget_->GetLayer()->GetTargetOpacity() &&
  1158. capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
  1159. location_in_screen);
  1160. if (capture_mode_settings_widget_ || is_event_on_capture_bar) {
  1161. cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
  1162. return;
  1163. }
  1164. const CaptureModeSource source = controller_->source();
  1165. if (source == CaptureModeSource::kWindow && !GetSelectedWindow()) {
  1166. // If we're in window capture mode and there is no select window at the
  1167. // moment, we should use a pointer cursor.
  1168. cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
  1169. return;
  1170. }
  1171. if (source == CaptureModeSource::kFullscreen ||
  1172. source == CaptureModeSource::kWindow) {
  1173. // For fullscreen and other window capture cases, we should either use
  1174. // image capture icon or screen record icon as the mouse icon.
  1175. cursor_setter_->UpdateCursor(GetCursorForFullscreenOrWindowCapture(
  1176. controller_->type() == CaptureModeType::kImage));
  1177. return;
  1178. }
  1179. DCHECK_EQ(source, CaptureModeSource::kRegion);
  1180. if (fine_tune_position_ != FineTunePosition::kNone) {
  1181. // We're in fine tuning process.
  1182. if (capture_mode_util::IsCornerFineTunePosition(fine_tune_position_)) {
  1183. cursor_setter_->HideCursor();
  1184. } else {
  1185. cursor_setter_->UpdateCursor(
  1186. GetCursorTypeForFineTunePosition(fine_tune_position_));
  1187. }
  1188. } else {
  1189. // Otherwise update the cursor depending on the current cursor location.
  1190. cursor_setter_->UpdateCursor(GetCursorTypeForFineTunePosition(
  1191. GetFineTunePosition(location_in_screen, is_touch)));
  1192. }
  1193. }
  1194. void CaptureModeSession::HighlightWindowForTab(aura::Window* window) {
  1195. DCHECK(window);
  1196. DCHECK_EQ(CaptureModeSource::kWindow, controller_->source());
  1197. MaybeChangeRoot(window->GetRootWindow());
  1198. capture_window_observer_->SetSelectedWindow(window);
  1199. }
  1200. void CaptureModeSession::MaybeUpdateSettingsBounds() {
  1201. if (!capture_mode_settings_widget_)
  1202. return;
  1203. capture_mode_settings_widget_->SetBounds(CaptureModeSettingsView::GetBounds(
  1204. capture_mode_bar_view_, capture_mode_settings_view_));
  1205. }
  1206. void CaptureModeSession::MaybeUpdateCaptureUisOpacity(
  1207. absl::optional<gfx::Point> cursor_screen_location) {
  1208. if (is_shutting_down_)
  1209. return;
  1210. // TODO(conniekxu): Handle this for tablet mode which doesn't have a cursor
  1211. // screen point.
  1212. if (!cursor_screen_location) {
  1213. cursor_screen_location =
  1214. display::Screen::GetScreen()->GetCursorScreenPoint();
  1215. }
  1216. base::flat_map<views::Widget*, /*opacity=*/float> widget_opacity_map;
  1217. if (capture_mode_bar_widget_)
  1218. widget_opacity_map[capture_mode_bar_widget_.get()] = 1.f;
  1219. if (capture_label_widget_)
  1220. widget_opacity_map[capture_label_widget_.get()] = 1.f;
  1221. const bool is_settings_visible = capture_mode_settings_widget_ &&
  1222. capture_mode_settings_widget_->IsVisible();
  1223. gfx::Rect capture_region = controller_->user_capture_region();
  1224. wm::ConvertRectToScreen(current_root_, &capture_region);
  1225. for (auto& pair : widget_opacity_map) {
  1226. views::Widget* widget = pair.first;
  1227. float& opacity = pair.second;
  1228. DCHECK(widget->GetLayer());
  1229. const gfx::Rect window_bounds_in_screen = widget->GetWindowBoundsInScreen();
  1230. const bool is_cursor_on_top_of_widget =
  1231. window_bounds_in_screen.Contains(*cursor_screen_location);
  1232. if (widget == capture_mode_bar_widget_.get()) {
  1233. // If capture setting is visible, capture bar should be fully opaque even
  1234. // if it's overlapped with camera preview.
  1235. if (is_settings_visible)
  1236. continue;
  1237. // If drag for capture region is in progress, capture bar should be
  1238. // hidden.
  1239. if (is_drag_in_progress_) {
  1240. opacity = 0.f;
  1241. continue;
  1242. }
  1243. if (is_cursor_on_top_of_widget)
  1244. continue;
  1245. // If the cursor is hovering on top of the capture label, capture bar
  1246. // should be fully opaque.
  1247. if (capture_label_widget_ &&
  1248. capture_label_widget_->GetWindowBoundsInScreen().Contains(
  1249. *cursor_screen_location)) {
  1250. continue;
  1251. }
  1252. const bool capture_bar_intersects_region =
  1253. controller_->source() == CaptureModeSource::kRegion &&
  1254. window_bounds_in_screen.Intersects(capture_region);
  1255. if (capture_bar_intersects_region) {
  1256. opacity = capture_mode::kCaptureUiOverlapOpacity;
  1257. continue;
  1258. }
  1259. if (focus_cycler_->CaptureBarFocused())
  1260. continue;
  1261. }
  1262. if (widget == capture_label_widget_.get() &&
  1263. (is_cursor_on_top_of_widget || focus_cycler_->CaptureLabelFocused())) {
  1264. continue;
  1265. }
  1266. if (IsWidgetOverlappedWithCameraPreview(widget))
  1267. opacity = capture_mode::kCaptureUiOverlapOpacity;
  1268. }
  1269. for (const auto& pair : widget_opacity_map) {
  1270. ui::Layer* layer = pair.first->GetLayer();
  1271. const float& opacity = pair.second;
  1272. if (layer->GetTargetOpacity() == opacity)
  1273. continue;
  1274. views::AnimationBuilder()
  1275. .SetPreemptionStrategy(
  1276. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  1277. .Once()
  1278. .SetDuration(kCaptureUIOpacityChangeDuration)
  1279. .SetOpacity(layer, opacity, gfx::Tween::FAST_OUT_SLOW_IN);
  1280. }
  1281. }
  1282. void CaptureModeSession::OnCameraPreviewDragStarted() {
  1283. DCHECK(!controller_->is_recording_in_progress());
  1284. // If settings menu is shown at the beginning of drag, we should close it.
  1285. if (capture_mode_settings_widget_)
  1286. SetSettingsMenuShown(false);
  1287. // Hide capture UIs while dragging camera preview.
  1288. HideAllUis();
  1289. }
  1290. void CaptureModeSession::OnCameraPreviewDragEnded(
  1291. const gfx::Point& screen_location,
  1292. bool is_touch) {
  1293. // When drag for camera preview is ended, camera preview will be snapped to
  1294. // one of the snap position, but cursor will leave at where the drag is
  1295. // released. In order to update cursor type correctly after camera preview is
  1296. // snapped, we should update `should_pass_located_event_to_camera_preview_` to
  1297. // false if cursor is not on top of camera preview, since `UpdateCursor` will
  1298. // rely on its value to decide whether cursor should be updated for camera
  1299. // preview.
  1300. auto* camera_preview_widget = GetCameraPreviewWidget();
  1301. DCHECK(camera_preview_widget);
  1302. if (!camera_preview_widget->GetWindowBoundsInScreen().Contains(
  1303. screen_location)) {
  1304. should_pass_located_event_to_camera_preview_ = false;
  1305. }
  1306. // If CaptureUIs (capture bar, capture label) are overlapped with camera
  1307. // preview and cursor is not on top of it, its opacity should be updated to
  1308. // `kCaptureUiOverlapOpacity` instead of fully opaque.
  1309. MaybeUpdateCaptureUisOpacity(screen_location);
  1310. // Show capture UIs which are hidden in `OnCameraPreviewDragStarted`.
  1311. ShowAllUis();
  1312. // Make sure cursor is updated correctly after camera preview is snapped.
  1313. UpdateCursor(screen_location, is_touch);
  1314. }
  1315. void CaptureModeSession::OnCameraPreviewBoundsOrVisibilityChanged(
  1316. bool capture_surface_became_too_small,
  1317. bool did_bounds_or_visibility_change) {
  1318. auto* camera_preview_widget = GetCameraPreviewWidget();
  1319. DCHECK(camera_preview_widget);
  1320. const bool is_parented_to_unparented_container =
  1321. camera_preview_widget->GetNativeWindow()->parent()->GetId() ==
  1322. kShellWindowId_UnparentedContainer;
  1323. if (capture_surface_became_too_small && !is_drag_in_progress_ &&
  1324. !is_parented_to_unparented_container) {
  1325. // Since the user nudge toast has lower priority, if the toast for the
  1326. // camera preview needs to be shown, user nudge toast should be dismissed
  1327. // forever when applicable.
  1328. MaybeDismissUserNudgeForever();
  1329. capture_toast_controller_.ShowCaptureToast(
  1330. CaptureToastType::kCameraPreview);
  1331. } else {
  1332. capture_toast_controller_.MaybeDismissCaptureToast(
  1333. CaptureToastType::kCameraPreview);
  1334. }
  1335. if (did_bounds_or_visibility_change)
  1336. MaybeUpdateCaptureUisOpacity();
  1337. }
  1338. void CaptureModeSession::OnCameraPreviewDestroyed() {
  1339. capture_toast_controller_.MaybeDismissCaptureToast(
  1340. CaptureToastType::kCameraPreview);
  1341. }
  1342. std::vector<views::Widget*> CaptureModeSession::GetAvailableWidgets() {
  1343. std::vector<views::Widget*> result;
  1344. DCHECK(capture_mode_bar_widget_);
  1345. result.push_back(capture_mode_bar_widget_.get());
  1346. if (capture_label_widget_)
  1347. result.push_back(capture_label_widget_.get());
  1348. if (capture_mode_settings_widget_)
  1349. result.push_back(capture_mode_settings_widget_.get());
  1350. if (dimensions_label_widget_)
  1351. result.push_back(dimensions_label_widget_.get());
  1352. if (auto* toast = capture_toast_controller_.capture_toast_widget())
  1353. result.push_back(toast);
  1354. return result;
  1355. }
  1356. void CaptureModeSession::HideAllUis() {
  1357. is_all_uis_visible_ = false;
  1358. cursor_setter_.reset();
  1359. for (auto* widget : GetAvailableWidgets()) {
  1360. // The order here matters. We need to disable the animation before we hide
  1361. // to avoid any hide animation here, or until the widgets are shown (also
  1362. // without animation) when ShowAllUis() is called.
  1363. widget->GetNativeWindow()->SetProperty(aura::client::kAnimationsDisabledKey,
  1364. true);
  1365. // The layer's opacity could be less than 1.f if the widget was hidden
  1366. // before we disabled the animations above. We need to reset the opacity
  1367. // back to 1.f as we will hide the widget without animation.
  1368. widget->GetLayer()->SetOpacity(1.f);
  1369. widget->Hide();
  1370. }
  1371. // Refresh painting the layer, since we don't paint anything while a DLP
  1372. // dialog might be shown.
  1373. layer()->SchedulePaint(layer()->bounds());
  1374. }
  1375. void CaptureModeSession::ShowAllUis() {
  1376. is_all_uis_visible_ = true;
  1377. cursor_setter_ = std::make_unique<CursorSetter>();
  1378. for (auto* widget : GetAvailableWidgets()) {
  1379. // The order here matters. See HideAllUis() above.
  1380. // At this point the animation is still disabled, so we show the window now
  1381. // before we re-enable the animations. This is to avoid having those widgets
  1382. // show up in the captured images or videos in case this is used right
  1383. // before ending the session to perform the capture.
  1384. if (CanShowWidget(widget))
  1385. widget->Show();
  1386. widget->GetNativeWindow()->SetProperty(aura::client::kAnimationsDisabledKey,
  1387. false);
  1388. }
  1389. layer()->SchedulePaint(layer()->bounds());
  1390. }
  1391. bool CaptureModeSession::CanShowWidget(views::Widget* widget) const {
  1392. // If `widget` is the toast widget, we shouldn't show it again in
  1393. // `ShowAllUis()` unless there is an available toast type, and the toast was
  1394. // never fully dismissed.
  1395. if (widget == capture_toast_controller_.capture_toast_widget())
  1396. return !!capture_toast_controller_.current_toast_type();
  1397. // If widget is the capture label widget, we will show it only if it doesn't
  1398. // intersect with the settings widget.
  1399. return !(capture_label_widget_ && capture_mode_settings_widget_ &&
  1400. capture_label_widget_.get() == widget &&
  1401. capture_mode_settings_widget_->GetWindowBoundsInScreen().Intersects(
  1402. capture_label_widget_->GetWindowBoundsInScreen()));
  1403. }
  1404. void CaptureModeSession::RefreshBarWidgetBounds() {
  1405. DCHECK(capture_mode_bar_widget_);
  1406. capture_mode_bar_widget_->SetBounds(
  1407. CaptureModeBarView::GetBounds(current_root_, is_in_projector_mode_));
  1408. auto* parent = GetParentContainer(current_root_);
  1409. parent->StackChildAtTop(capture_mode_bar_widget_->GetNativeWindow());
  1410. if (user_nudge_controller_)
  1411. user_nudge_controller_->Reposition();
  1412. capture_toast_controller_.MaybeRepositionCaptureToast();
  1413. }
  1414. void CaptureModeSession::MaybeCreateUserNudge() {
  1415. user_nudge_controller_.reset();
  1416. if (is_in_projector_mode_)
  1417. return;
  1418. if (!controller_->CanShowUserNudge())
  1419. return;
  1420. user_nudge_controller_ = std::make_unique<UserNudgeController>(
  1421. this, capture_mode_bar_view_->settings_button());
  1422. user_nudge_controller_->SetVisible(true);
  1423. }
  1424. void CaptureModeSession::MaybeDismissUserNudgeForever() {
  1425. if (user_nudge_controller_)
  1426. user_nudge_controller_->set_should_dismiss_nudge_forever(true);
  1427. user_nudge_controller_.reset();
  1428. }
  1429. void CaptureModeSession::DoPerformCapture() {
  1430. MaybeDismissUserNudgeForever();
  1431. controller_->PerformCapture(); // `this` can be deleted after this.
  1432. }
  1433. gfx::Rect CaptureModeSession::GetSelectedWindowBounds() const {
  1434. auto* window = GetSelectedWindow();
  1435. return window ? window->bounds() : gfx::Rect();
  1436. }
  1437. void CaptureModeSession::RefreshStackingOrder() {
  1438. if (is_shutting_down_)
  1439. return;
  1440. auto* parent_container = GetParentContainer(current_root_);
  1441. DCHECK(parent_container);
  1442. auto* session_layer = layer();
  1443. auto* parent_container_layer = parent_container->layer();
  1444. parent_container_layer->StackAtTop(session_layer);
  1445. std::vector<views::Widget*> widget_in_order;
  1446. auto* camera_preview_widget = GetCameraPreviewWidget();
  1447. // We don't need to update the stacking order for camera preview if
  1448. // there's a video recording in progress, since the camera preview don't
  1449. // belong to the current capture session.
  1450. if (camera_preview_widget && !controller_->is_recording_in_progress())
  1451. widget_in_order.emplace_back(camera_preview_widget);
  1452. if (auto* toast = capture_toast_controller_.capture_toast_widget())
  1453. widget_in_order.emplace_back(toast);
  1454. if (capture_label_widget_)
  1455. widget_in_order.emplace_back(capture_label_widget_.get());
  1456. if (capture_mode_bar_widget_)
  1457. widget_in_order.emplace_back(capture_mode_bar_widget_.get());
  1458. if (capture_mode_settings_widget_)
  1459. widget_in_order.emplace_back(capture_mode_settings_widget_.get());
  1460. for (auto* widget : widget_in_order) {
  1461. auto* widget_window = widget->GetNativeWindow();
  1462. // Make sure the order of `widget` layer and the order of `widget` window
  1463. // match. Also notice we should stack layer later since when stacking
  1464. // window, it will also stack window's layer which may mess the layer's
  1465. // order if we stack layer first.
  1466. if (widget_window->parent() == parent_container) {
  1467. parent_container->StackChildAtTop(widget_window);
  1468. parent_container_layer->StackAtTop(widget_window->layer());
  1469. }
  1470. }
  1471. }
  1472. void CaptureModeSession::PaintCaptureRegion(gfx::Canvas* canvas) {
  1473. gfx::Rect region;
  1474. bool adjustable_region = false;
  1475. switch (controller_->source()) {
  1476. case CaptureModeSource::kFullscreen:
  1477. region = current_root_->bounds();
  1478. break;
  1479. case CaptureModeSource::kWindow:
  1480. region = GetSelectedWindowBounds();
  1481. break;
  1482. case CaptureModeSource::kRegion:
  1483. region = controller_->user_capture_region();
  1484. adjustable_region = true;
  1485. break;
  1486. }
  1487. if (region.IsEmpty())
  1488. return;
  1489. gfx::ScopedCanvas scoped_canvas(canvas);
  1490. const float dsf = canvas->UndoDeviceScaleFactor();
  1491. region = gfx::ScaleToEnclosingRect(region, dsf);
  1492. if (!adjustable_region) {
  1493. canvas->FillRect(region, SK_ColorTRANSPARENT, SkBlendMode::kClear);
  1494. canvas->FillRect(
  1495. region, AshColorProvider::Get()->GetContentLayerColor(
  1496. AshColorProvider::ContentLayerType::kCaptureRegionColor));
  1497. return;
  1498. }
  1499. region.Inset(-capture_mode::kCaptureRegionBorderStrokePx);
  1500. canvas->FillRect(region, SK_ColorTRANSPARENT, SkBlendMode::kClear);
  1501. // Draw the region border.
  1502. cc::PaintFlags border_flags;
  1503. border_flags.setColor(capture_mode::kRegionBorderColor);
  1504. border_flags.setStyle(cc::PaintFlags::kStroke_Style);
  1505. border_flags.setStrokeWidth(capture_mode::kCaptureRegionBorderStrokePx);
  1506. border_flags.setLooper(gfx::CreateShadowDrawLooper({kRegionOutlineShadow}));
  1507. canvas->DrawRect(gfx::RectF(region), border_flags);
  1508. // Draws the focus ring if the region or one of the affordance circles
  1509. // currently has focus.
  1510. auto maybe_draw_focus_ring = [&canvas, &region,
  1511. &dsf](FineTunePosition position) {
  1512. if (position == FineTunePosition::kNone)
  1513. return;
  1514. cc::PaintFlags focus_ring_flags;
  1515. focus_ring_flags.setAntiAlias(true);
  1516. focus_ring_flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
  1517. AshColorProvider::ControlsLayerType::kFocusRingColor));
  1518. focus_ring_flags.setStyle(cc::PaintFlags::kStroke_Style);
  1519. focus_ring_flags.setStrokeWidth(kFocusRingStrokeWidthDp);
  1520. if (position == FineTunePosition::kCenter) {
  1521. gfx::RectF focus_rect(region);
  1522. focus_rect.Inset(
  1523. gfx::InsetsF(-kFocusRingSpacingDp - kFocusRingStrokeWidthDp / 2));
  1524. canvas->DrawRect(focus_rect, focus_ring_flags);
  1525. return;
  1526. }
  1527. const float radius =
  1528. dsf * (kAffordanceCircleRadiusDp + kFocusRingSpacingDp +
  1529. kFocusRingStrokeWidthDp / 2);
  1530. canvas->DrawCircle(
  1531. capture_mode_util::GetLocationForFineTunePosition(region, position),
  1532. radius, focus_ring_flags);
  1533. };
  1534. const FineTunePosition focused_fine_tune_position =
  1535. focus_cycler_->GetFocusedFineTunePosition();
  1536. if (is_selecting_region_ || fine_tune_position_ != FineTunePosition::kNone) {
  1537. maybe_draw_focus_ring(focused_fine_tune_position);
  1538. return;
  1539. }
  1540. if (IsInCountDownAnimation())
  1541. return;
  1542. // Draw the drag affordance circles.
  1543. cc::PaintFlags circle_flags;
  1544. circle_flags.setColor(capture_mode::kRegionBorderColor);
  1545. circle_flags.setStyle(cc::PaintFlags::kFill_Style);
  1546. circle_flags.setAntiAlias(true);
  1547. circle_flags.setLooper(gfx::CreateShadowDrawLooper(
  1548. {kRegionAffordanceCircleShadow1, kRegionAffordanceCircleShadow2}));
  1549. auto draw_circle = [&canvas, &circle_flags,
  1550. &dsf](const gfx::Point& location) {
  1551. canvas->DrawCircle(location, dsf * kAffordanceCircleRadiusDp, circle_flags);
  1552. };
  1553. draw_circle(region.origin());
  1554. draw_circle(region.top_center());
  1555. draw_circle(region.top_right());
  1556. draw_circle(region.right_center());
  1557. draw_circle(region.bottom_right());
  1558. draw_circle(region.bottom_center());
  1559. draw_circle(region.bottom_left());
  1560. draw_circle(region.left_center());
  1561. maybe_draw_focus_ring(focused_fine_tune_position);
  1562. }
  1563. void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
  1564. bool is_touch) {
  1565. if (folder_selection_dialog_controller_) {
  1566. if (folder_selection_dialog_controller_->ShouldConsumeEvent(event))
  1567. event->StopPropagation();
  1568. return;
  1569. }
  1570. // If we're currently in countdown animation, don't further handle any
  1571. // located events. However we should stop the event propagation here to
  1572. // prevent other event handlers from handling this event.
  1573. if (IsInCountDownAnimation()) {
  1574. event->StopPropagation();
  1575. return;
  1576. }
  1577. // |ui::ET_MOUSE_EXITED| and |ui::ET_MOUSE_ENTERED| events will be generated
  1578. // during moving capture mode bar to another display. We should ignore them
  1579. // here, since they will overwrite the capture mode bar's root change during
  1580. // keyboard tabbing in capture window mode.
  1581. if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED ||
  1582. event->type() == ui::ET_MOUSE_EXITED ||
  1583. event->type() == ui::ET_MOUSE_ENTERED) {
  1584. return;
  1585. }
  1586. // We should ignore synthesized events here. Otherwise, synthesized events
  1587. // will overwrite the change by the actual event because of the
  1588. // asynchronism (please check |WindowEventDispatcher::PostSynthesizeMouseMove|
  1589. // for more information).
  1590. // For example, during keyboard navigation in capture window mode, changing
  1591. // root of capture mode bar will generate the synthesized mouse move event. It
  1592. // will overwrite the root change since the location of the synthesized event
  1593. // is still on the previous root.
  1594. // For the window related synthesized events (window activation, window
  1595. // destroy), |capture_window_observer_| can take care of them.
  1596. if (event->flags() & ui::EF_IS_SYNTHESIZED)
  1597. return;
  1598. gfx::Point screen_location = event->location();
  1599. aura::Window* event_target = static_cast<aura::Window*>(event->target());
  1600. wm::ConvertPointToScreen(event_target, &screen_location);
  1601. // For fullscreen/window mode, change the root window as soon as we detect the
  1602. // cursor on a new display. For region mode, wait until the user taps down to
  1603. // try to select a new region on the new display.
  1604. const CaptureModeSource capture_source = controller_->source();
  1605. const bool is_capture_region = capture_source == CaptureModeSource::kRegion;
  1606. const bool is_press_event = event->type() == ui::ET_MOUSE_PRESSED ||
  1607. event->type() == ui::ET_TOUCH_PRESSED;
  1608. // Clear keyboard focus on presses.
  1609. if (is_press_event && focus_cycler_->HasFocus())
  1610. focus_cycler_->ClearFocus();
  1611. const bool can_change_root = !is_capture_region || is_press_event;
  1612. if (can_change_root)
  1613. MaybeChangeRoot(GetPreferredRootWindow(screen_location));
  1614. // The root may have switched while pressing the mouse down. Move the capture
  1615. // bar to the current display if that is the case and make sure it is stacked
  1616. // at the top. The dimensions label and capture button have been moved and
  1617. // stacked on tap down so manually stack at top instead of calling
  1618. // RefreshStackingOrder.
  1619. const bool is_release_event = event->type() == ui::ET_MOUSE_RELEASED ||
  1620. event->type() == ui::ET_TOUCH_RELEASED;
  1621. if (is_release_event && is_capture_region &&
  1622. current_root_ !=
  1623. capture_mode_bar_widget_->GetNativeWindow()->GetRootWindow()) {
  1624. RefreshBarWidgetBounds();
  1625. }
  1626. MaybeUpdateCaptureUisOpacity(screen_location);
  1627. // Update the value of `should_pass_located_event_to_camera_preview_` here
  1628. // before calling `UpdateCursor` which uses it.
  1629. should_pass_located_event_to_camera_preview_ =
  1630. ShouldPassEventToCameraPreview(event);
  1631. // From here on, no matter where the function exists, the cursor must be
  1632. // updated at the end.
  1633. base::ScopedClosureRunner deferred_cursor_updater(base::BindOnce(
  1634. &CaptureModeSession::UpdateCursor, weak_ptr_factory_.GetWeakPtr(),
  1635. screen_location, is_touch));
  1636. if (should_pass_located_event_to_camera_preview_) {
  1637. DCHECK(!controller_->is_recording_in_progress());
  1638. return;
  1639. }
  1640. // Let the capture button handle any events it can handle first.
  1641. if (ShouldCaptureLabelHandleEvent(event_target))
  1642. return;
  1643. // Also allow events that target the settings menu (if present) to go through.
  1644. if (IsEventTargetedOnSettingsMenu(*event))
  1645. return;
  1646. // Here we know that the event doesn't target the settings menu, so if it's a
  1647. // press event, we will use it to dismiss the settings menu, unless it's on
  1648. // the settings button (since in this case, the settings button handler will
  1649. // take care of dismissing the menu).
  1650. const bool is_event_on_settings_button =
  1651. capture_mode_bar_view_->settings_button()->GetBoundsInScreen().Contains(
  1652. screen_location);
  1653. const bool should_close_settings = is_press_event &&
  1654. !is_event_on_settings_button &&
  1655. capture_mode_settings_widget_;
  1656. if (should_close_settings) {
  1657. // All future located events up to and including a released events will be
  1658. // consumed and ignored (i.e. won't be used to update the capture region,
  1659. // the selected window, or perform capture ... etc.), unless it's targeting
  1660. // the capture button.
  1661. ignore_located_events_ = true;
  1662. SetSettingsMenuShown(/*shown=*/false);
  1663. }
  1664. const bool old_ignore_located_events = ignore_located_events_;
  1665. if (ignore_located_events_) {
  1666. if (is_release_event)
  1667. ignore_located_events_ = false;
  1668. }
  1669. // Events targeting the capture bar should also go through.
  1670. if (IsEventTargetedOnCaptureBar(*event))
  1671. return;
  1672. event->SetHandled();
  1673. event->StopPropagation();
  1674. if (should_close_settings || old_ignore_located_events) {
  1675. // Note that these ignored events have already been consumed above.
  1676. return;
  1677. }
  1678. const bool is_capture_fullscreen =
  1679. capture_source == CaptureModeSource::kFullscreen;
  1680. const bool is_capture_window = capture_source == CaptureModeSource::kWindow;
  1681. if (is_capture_fullscreen || is_capture_window) {
  1682. switch (event->type()) {
  1683. case ui::ET_MOUSE_MOVED:
  1684. case ui::ET_TOUCH_PRESSED:
  1685. case ui::ET_TOUCH_MOVED: {
  1686. if (is_capture_window) {
  1687. capture_window_observer_->UpdateSelectedWindowAtPosition(
  1688. screen_location, {});
  1689. }
  1690. break;
  1691. }
  1692. case ui::ET_MOUSE_RELEASED:
  1693. case ui::ET_TOUCH_RELEASED:
  1694. if (is_capture_fullscreen ||
  1695. (is_capture_window && GetSelectedWindow())) {
  1696. DoPerformCapture(); // `this` can be deleted after this.
  1697. }
  1698. break;
  1699. default:
  1700. break;
  1701. }
  1702. return;
  1703. }
  1704. DCHECK(is_capture_region);
  1705. // `OnLocatedEventPressed()` and `OnLocatedEventDragged()` used root locations
  1706. // since `CaptureModeController::user_capture_region()` is stored in root
  1707. // coordinates.
  1708. const gfx::Point& location_in_root = event->root_location();
  1709. switch (event->type()) {
  1710. case ui::ET_MOUSE_PRESSED:
  1711. case ui::ET_TOUCH_PRESSED:
  1712. old_mouse_warp_status_ = SetMouseWarpEnabled(false);
  1713. OnLocatedEventPressed(location_in_root, is_touch);
  1714. break;
  1715. case ui::ET_MOUSE_DRAGGED:
  1716. case ui::ET_TOUCH_MOVED:
  1717. OnLocatedEventDragged(location_in_root);
  1718. break;
  1719. case ui::ET_MOUSE_RELEASED:
  1720. case ui::ET_TOUCH_RELEASED:
  1721. // Reenable mouse warping.
  1722. if (old_mouse_warp_status_)
  1723. SetMouseWarpEnabled(*old_mouse_warp_status_);
  1724. old_mouse_warp_status_.reset();
  1725. OnLocatedEventReleased(location_in_root);
  1726. break;
  1727. default:
  1728. break;
  1729. }
  1730. }
  1731. FineTunePosition CaptureModeSession::GetFineTunePosition(
  1732. const gfx::Point& location_in_screen,
  1733. bool is_touch) const {
  1734. // When the region is empty, this is a brand new selection rather than a fine
  1735. // tune.
  1736. if (controller_->user_capture_region().IsEmpty())
  1737. return FineTunePosition::kNone;
  1738. gfx::Rect capture_region_in_screen = controller_->user_capture_region();
  1739. wm::ConvertRectToScreen(current_root_, &capture_region_in_screen);
  1740. // In the case of overlapping affordances, prioritize the bottomm right
  1741. // corner, then the rest of the corners, then the edges.
  1742. static const std::vector<FineTunePosition> drag_positions = {
  1743. FineTunePosition::kBottomRight, FineTunePosition::kBottomLeft,
  1744. FineTunePosition::kTopLeft, FineTunePosition::kTopRight,
  1745. FineTunePosition::kBottomCenter, FineTunePosition::kLeftCenter,
  1746. FineTunePosition::kTopCenter, FineTunePosition::kRightCenter};
  1747. const int hit_radius =
  1748. is_touch ? kAffordanceCircleTouchHitRadiusDp : kAffordanceCircleRadiusDp;
  1749. const int hit_radius_squared = hit_radius * hit_radius;
  1750. for (FineTunePosition position : drag_positions) {
  1751. const gfx::Point position_location =
  1752. capture_mode_util::GetLocationForFineTunePosition(
  1753. capture_region_in_screen, position);
  1754. // If `location_in_screen` is within `hit_radius` of `position_location` for
  1755. // both x and y, then `position` is the current pressed down affordance.
  1756. if ((position_location - location_in_screen).LengthSquared() <=
  1757. hit_radius_squared) {
  1758. return position;
  1759. }
  1760. }
  1761. if (capture_region_in_screen.Contains(location_in_screen))
  1762. return FineTunePosition::kCenter;
  1763. return FineTunePosition::kNone;
  1764. }
  1765. void CaptureModeSession::OnLocatedEventPressed(
  1766. const gfx::Point& location_in_root,
  1767. bool is_touch) {
  1768. initial_location_in_root_ = location_in_root;
  1769. previous_location_in_root_ = location_in_root;
  1770. // Use cursor compositing instead of the platform cursor when dragging to
  1771. // ensure the cursor is aligned with the region.
  1772. is_drag_in_progress_ = true;
  1773. Shell::Get()->UpdateCursorCompositingEnabled();
  1774. if (user_nudge_controller_)
  1775. user_nudge_controller_->SetVisible(false);
  1776. gfx::Point screen_location = location_in_root;
  1777. wm::ConvertPointToScreen(current_root_, &screen_location);
  1778. MaybeUpdateCaptureUisOpacity(screen_location);
  1779. // Run `MaybeUpdateCameraPreviewBounds` at the exit of this function's
  1780. // scope since the camera preview should be hidden if user is dragging to
  1781. // update the capture region. The reason we want to run it at the exit of this
  1782. // function is if `is_selecting_region_` is false, we want
  1783. // `fine_tune_position_` to be updated first since it can affect whether we
  1784. // should hide camera preview or not.
  1785. base::ScopedClosureRunner deferred_runner(
  1786. base::BindOnce(&CaptureModeSession::MaybeUpdateCameraPreviewBounds,
  1787. weak_ptr_factory_.GetWeakPtr()));
  1788. if (is_selecting_region_)
  1789. return;
  1790. fine_tune_position_ = GetFineTunePosition(screen_location, is_touch);
  1791. if (fine_tune_position_ == FineTunePosition::kNone) {
  1792. // If the point is outside the capture region and not on the capture bar or
  1793. // settings menu, restart to the select phase.
  1794. is_selecting_region_ = true;
  1795. UpdateCaptureRegion(gfx::Rect(), /*is_resizing=*/true, /*by_user=*/true);
  1796. num_capture_region_adjusted_ = 0;
  1797. return;
  1798. }
  1799. // In order to hide the drag affordance circles on click, we need to repaint
  1800. // the capture region.
  1801. if (fine_tune_position_ != FineTunePosition::kNone) {
  1802. ++num_capture_region_adjusted_;
  1803. RepaintRegion();
  1804. }
  1805. if (fine_tune_position_ != FineTunePosition::kCenter &&
  1806. fine_tune_position_ != FineTunePosition::kNone) {
  1807. anchor_points_ = GetAnchorPointsForPosition(fine_tune_position_);
  1808. const gfx::Point position_location =
  1809. capture_mode_util::GetLocationForFineTunePosition(
  1810. controller_->user_capture_region(), fine_tune_position_);
  1811. MaybeShowMagnifierGlassAtPoint(position_location);
  1812. }
  1813. }
  1814. void CaptureModeSession::OnLocatedEventDragged(
  1815. const gfx::Point& location_in_root) {
  1816. const gfx::Point previous_location_in_root = previous_location_in_root_;
  1817. previous_location_in_root_ = location_in_root;
  1818. // For the select phase, the select region is the rectangle formed by the
  1819. // press location and the current location.
  1820. if (is_selecting_region_) {
  1821. UpdateCaptureRegion(
  1822. GetRectEnclosingPoints({initial_location_in_root_, location_in_root}),
  1823. /*is_resizing=*/true, /*by_user=*/true);
  1824. return;
  1825. }
  1826. if (fine_tune_position_ == FineTunePosition::kNone)
  1827. return;
  1828. // For a reposition, offset the old select region by the difference between
  1829. // the current location and the previous location, but do not let the select
  1830. // region go offscreen.
  1831. if (fine_tune_position_ == FineTunePosition::kCenter) {
  1832. gfx::Rect new_capture_region = controller_->user_capture_region();
  1833. new_capture_region.Offset(location_in_root - previous_location_in_root);
  1834. new_capture_region.AdjustToFit(current_root_->bounds());
  1835. UpdateCaptureRegion(new_capture_region, /*is_resizing=*/false,
  1836. /*by_user=*/true);
  1837. return;
  1838. }
  1839. // The new region is defined by the rectangle which encloses the anchor
  1840. // point(s) and |resizing_point|, which is based off of |location_in_root| but
  1841. // prevents edge drags from resizing the region in the non-desired direction.
  1842. std::vector<gfx::Point> points = anchor_points_;
  1843. DCHECK(!points.empty());
  1844. gfx::Point resizing_point = location_in_root;
  1845. // For edge dragging, there will be two anchor points with the same primary
  1846. // axis value. Setting |resizing_point|'s secondary axis value to match either
  1847. // one of the anchor points secondary axis value will ensure that for the
  1848. // duration of a drag, GetRectEnclosingPoints will return a rect whose
  1849. // secondary dimension does not change.
  1850. if (fine_tune_position_ == FineTunePosition::kLeftCenter ||
  1851. fine_tune_position_ == FineTunePosition::kRightCenter) {
  1852. resizing_point.set_y(points.front().y());
  1853. } else if (fine_tune_position_ == FineTunePosition::kTopCenter ||
  1854. fine_tune_position_ == FineTunePosition::kBottomCenter) {
  1855. resizing_point.set_x(points.front().x());
  1856. }
  1857. points.push_back(resizing_point);
  1858. UpdateCaptureRegion(GetRectEnclosingPoints(points), /*is_resizing=*/true,
  1859. /*by_user=*/true);
  1860. MaybeShowMagnifierGlassAtPoint(location_in_root);
  1861. }
  1862. void CaptureModeSession::OnLocatedEventReleased(
  1863. const gfx::Point& location_in_root) {
  1864. // TODO(conniekxu): Handle the opacity of the toast widget when it's
  1865. // overlapped with the capture region.
  1866. if (user_nudge_controller_)
  1867. user_nudge_controller_->SetVisible(true);
  1868. gfx::Point screen_location = location_in_root;
  1869. wm::ConvertPointToScreen(current_root_, &screen_location);
  1870. EndSelection(screen_location);
  1871. // Do a repaint to show the affordance circles.
  1872. RepaintRegion();
  1873. // Run `MaybeUpdateCameraPreviewBounds` when user releases the drag at
  1874. // the exit of this function's scope to show the camera preview which may have
  1875. // been hidden in `OnLocatedEventPressed`. The reason we want to run it at the
  1876. // exit of this function is if `is_selecting_region_` is true, we want to wait
  1877. // until the capture label is updated since capture label's opacity may need
  1878. // to be updated based on if it's overlapped with camera preview or not.
  1879. base::ScopedClosureRunner deferred_runner(
  1880. base::BindOnce(&CaptureModeSession::MaybeUpdateCameraPreviewBounds,
  1881. weak_ptr_factory_.GetWeakPtr()));
  1882. if (!is_selecting_region_)
  1883. return;
  1884. // After first release event, we advance to the next phase.
  1885. is_selecting_region_ = false;
  1886. UpdateCaptureLabelWidget(CaptureLabelAnimation::kRegionPhaseChange);
  1887. A11yAlertCaptureSource(/*trigger_now=*/true);
  1888. }
  1889. void CaptureModeSession::UpdateCaptureRegion(
  1890. const gfx::Rect& new_capture_region,
  1891. bool is_resizing,
  1892. bool by_user) {
  1893. const gfx::Rect old_capture_region = controller_->user_capture_region();
  1894. if (old_capture_region == new_capture_region)
  1895. return;
  1896. // Calculate the region that has been damaged and repaint the layer. Add some
  1897. // extra padding to make sure the border and affordance circles are also
  1898. // repainted.
  1899. gfx::Rect damage_region = old_capture_region;
  1900. damage_region.Union(new_capture_region);
  1901. damage_region.Inset(gfx::Insets(-kDamageInsetDp));
  1902. layer()->SchedulePaint(damage_region);
  1903. controller_->SetUserCaptureRegion(new_capture_region, by_user);
  1904. UpdateDimensionsLabelWidget(is_resizing);
  1905. UpdateCaptureLabelWidget(CaptureLabelAnimation::kNone);
  1906. }
  1907. void CaptureModeSession::UpdateDimensionsLabelWidget(bool is_resizing) {
  1908. const bool should_not_show =
  1909. !is_resizing || controller_->source() != CaptureModeSource::kRegion ||
  1910. controller_->user_capture_region().IsEmpty();
  1911. if (should_not_show) {
  1912. dimensions_label_widget_.reset();
  1913. return;
  1914. }
  1915. if (!dimensions_label_widget_) {
  1916. auto* parent = GetParentContainer(current_root_);
  1917. dimensions_label_widget_ = std::make_unique<views::Widget>();
  1918. dimensions_label_widget_->Init(
  1919. CreateWidgetParams(parent, gfx::Rect(), "CaptureModeDimensionsLabel"));
  1920. auto size_label = std::make_unique<views::Label>();
  1921. auto* color_provider = AshColorProvider::Get();
  1922. size_label->SetEnabledColor(color_provider->GetContentLayerColor(
  1923. AshColorProvider::ContentLayerType::kTextColorPrimary));
  1924. size_label->SetBackground(views::CreateRoundedRectBackground(
  1925. color_provider->GetBaseLayerColor(
  1926. AshColorProvider::BaseLayerType::kTransparent80),
  1927. kSizeLabelBorderRadius));
  1928. size_label->SetAutoColorReadabilityEnabled(false);
  1929. dimensions_label_widget_->SetContentsView(std::move(size_label));
  1930. dimensions_label_widget_->Show();
  1931. // When moving to a new display, the dimensions label gets created/moved
  1932. // onto the new display on press, while the capture bar gets moved on
  1933. // release. In this case, we do not have to stack the dimensions label.
  1934. if (parent == capture_mode_bar_widget_->GetNativeWindow()->parent()) {
  1935. parent->StackChildBelow(dimensions_label_widget_->GetNativeWindow(),
  1936. capture_mode_bar_widget_->GetNativeWindow());
  1937. }
  1938. }
  1939. views::Label* size_label =
  1940. static_cast<views::Label*>(dimensions_label_widget_->GetContentsView());
  1941. const gfx::Rect capture_region = controller_->user_capture_region();
  1942. size_label->SetText(base::UTF8ToUTF16(base::StringPrintf(
  1943. "%d x %d", capture_region.width(), capture_region.height())));
  1944. UpdateDimensionsLabelBounds();
  1945. }
  1946. void CaptureModeSession::UpdateDimensionsLabelBounds() {
  1947. DCHECK(dimensions_label_widget_ &&
  1948. dimensions_label_widget_->GetContentsView());
  1949. gfx::Rect bounds(
  1950. dimensions_label_widget_->GetContentsView()->GetPreferredSize());
  1951. const gfx::Rect capture_region = controller_->user_capture_region();
  1952. gfx::Rect screen_region = current_root_->bounds();
  1953. bounds.set_width(bounds.width() + 2 * kSizeLabelHorizontalPadding);
  1954. bounds.set_x(capture_region.CenterPoint().x() - bounds.width() / 2);
  1955. bounds.set_y(capture_region.bottom() + kSizeLabelYDistanceFromRegionDp);
  1956. // The dimension label should always be within the screen and at the bottom of
  1957. // the capture region. If it does not fit below the bottom edge fo the region,
  1958. // move it above the bottom edge into the capture region.
  1959. screen_region.Inset(
  1960. gfx::Insets::TLBR(0, 0, kSizeLabelYDistanceFromRegionDp, 0));
  1961. bounds.AdjustToFit(screen_region);
  1962. wm::ConvertRectToScreen(current_root_, &bounds);
  1963. dimensions_label_widget_->SetBounds(bounds);
  1964. }
  1965. void CaptureModeSession::MaybeShowMagnifierGlassAtPoint(
  1966. const gfx::Point& location_in_root) {
  1967. if (!capture_mode_util::IsCornerFineTunePosition(fine_tune_position_))
  1968. return;
  1969. magnifier_glass_.ShowFor(current_root_, location_in_root);
  1970. }
  1971. void CaptureModeSession::CloseMagnifierGlass() {
  1972. magnifier_glass_.Close();
  1973. }
  1974. std::vector<gfx::Point> CaptureModeSession::GetAnchorPointsForPosition(
  1975. FineTunePosition position) {
  1976. std::vector<gfx::Point> anchor_points;
  1977. // For a vertex, the anchor point is the opposite vertex on the rectangle
  1978. // (ex. bottom left vertex -> top right vertex anchor point). For an edge, the
  1979. // anchor points are the two vertices of the opposite edge (ex. bottom edge ->
  1980. // top left and top right anchor points).
  1981. const gfx::Rect rect = controller_->user_capture_region();
  1982. switch (position) {
  1983. case FineTunePosition::kNone:
  1984. case FineTunePosition::kCenter:
  1985. break;
  1986. case FineTunePosition::kTopLeft:
  1987. anchor_points.push_back(rect.bottom_right());
  1988. break;
  1989. case FineTunePosition::kTopCenter:
  1990. anchor_points.push_back(rect.bottom_left());
  1991. anchor_points.push_back(rect.bottom_right());
  1992. break;
  1993. case FineTunePosition::kTopRight:
  1994. anchor_points.push_back(rect.bottom_left());
  1995. break;
  1996. case FineTunePosition::kLeftCenter:
  1997. anchor_points.push_back(rect.top_right());
  1998. anchor_points.push_back(rect.bottom_right());
  1999. break;
  2000. case FineTunePosition::kRightCenter:
  2001. anchor_points.push_back(rect.origin());
  2002. anchor_points.push_back(rect.bottom_left());
  2003. break;
  2004. case FineTunePosition::kBottomLeft:
  2005. anchor_points.push_back(rect.top_right());
  2006. break;
  2007. case FineTunePosition::kBottomCenter:
  2008. anchor_points.push_back(rect.origin());
  2009. anchor_points.push_back(rect.top_right());
  2010. break;
  2011. case FineTunePosition::kBottomRight:
  2012. anchor_points.push_back(rect.origin());
  2013. break;
  2014. }
  2015. DCHECK(!anchor_points.empty());
  2016. DCHECK_LE(anchor_points.size(), 2u);
  2017. return anchor_points;
  2018. }
  2019. void CaptureModeSession::UpdateCaptureLabelWidget(
  2020. CaptureLabelAnimation animation_type) {
  2021. if (!capture_label_widget_) {
  2022. capture_label_widget_ = std::make_unique<views::Widget>();
  2023. auto* parent = GetParentContainer(current_root_);
  2024. capture_label_widget_->Init(
  2025. CreateWidgetParams(parent, gfx::Rect(), "CaptureLabel"));
  2026. capture_label_widget_->SetContentsView(std::make_unique<CaptureLabelView>(
  2027. this, base::BindRepeating(&CaptureModeSession::DoPerformCapture,
  2028. base::Unretained(this))));
  2029. capture_label_widget_->GetNativeWindow()->SetTitle(
  2030. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_A11Y_TITLE));
  2031. capture_label_widget_->Show();
  2032. }
  2033. CaptureLabelView* label_view =
  2034. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
  2035. label_view->UpdateIconAndText();
  2036. UpdateCaptureLabelWidgetBounds(animation_type);
  2037. focus_cycler_->OnCaptureLabelWidgetUpdated();
  2038. }
  2039. void CaptureModeSession::UpdateCaptureLabelWidgetBounds(
  2040. CaptureLabelAnimation animation_type) {
  2041. DCHECK(capture_label_widget_);
  2042. const gfx::Rect bounds = CalculateCaptureLabelWidgetBounds();
  2043. const gfx::Rect old_bounds =
  2044. capture_label_widget_->GetNativeWindow()->GetBoundsInScreen();
  2045. if (old_bounds == bounds)
  2046. return;
  2047. if (animation_type == CaptureLabelAnimation::kNone) {
  2048. capture_label_widget_->SetBounds(bounds);
  2049. return;
  2050. }
  2051. ui::Layer* layer = capture_label_widget_->GetLayer();
  2052. ui::LayerAnimator* animator = layer->GetAnimator();
  2053. if (animation_type == CaptureLabelAnimation::kRegionPhaseChange) {
  2054. capture_label_widget_->SetBounds(bounds);
  2055. const gfx::Point center_point = bounds.CenterPoint();
  2056. layer->SetTransform(
  2057. gfx::GetScaleTransform(gfx::Point(center_point.x() - bounds.x(),
  2058. center_point.y() - bounds.y()),
  2059. kLabelScaleDownOnPhaseChange));
  2060. layer->SetOpacity(0.f);
  2061. ui::ScopedLayerAnimationSettings settings(animator);
  2062. settings.SetTransitionDuration(kCaptureLabelRegionPhaseChangeDuration);
  2063. settings.SetTweenType(gfx::Tween::ACCEL_LIN_DECEL_100);
  2064. settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
  2065. animator->SchedulePauseForProperties(
  2066. kCaptureLabelRegionPhaseChangeDelay,
  2067. ui::LayerAnimationElement::TRANSFORM |
  2068. ui::LayerAnimationElement::OPACITY);
  2069. layer->SetTransform(gfx::Transform());
  2070. layer->SetOpacity(1.f);
  2071. return;
  2072. }
  2073. DCHECK_EQ(CaptureLabelAnimation::kCountdownStart, animation_type);
  2074. if (!old_bounds.IsEmpty()) {
  2075. // This happens if there is a label or a label button showing when count
  2076. // down starts. In this case we'll do a bounds change animation.
  2077. ui::ScopedLayerAnimationSettings settings(animator);
  2078. settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
  2079. settings.SetPreemptionStrategy(
  2080. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  2081. settings.SetTransitionDuration(kCaptureLabelCountdownStartDuration);
  2082. capture_label_widget_->SetBounds(bounds);
  2083. } else {
  2084. // This happens when no text message was showing when count down starts, in
  2085. // this case we'll do a fade in + shrinking down animation.
  2086. capture_label_widget_->SetBounds(bounds);
  2087. const gfx::Point center_point = bounds.CenterPoint();
  2088. layer->SetTransform(
  2089. gfx::GetScaleTransform(gfx::Point(center_point.x() - bounds.x(),
  2090. center_point.y() - bounds.y()),
  2091. kLabelScaleUpOnCountdown));
  2092. layer->SetOpacity(0.f);
  2093. // Fade in.
  2094. ui::ScopedLayerAnimationSettings settings(animator);
  2095. settings.SetTransitionDuration(kCaptureLabelCountdownStartDuration);
  2096. settings.SetTweenType(gfx::Tween::LINEAR);
  2097. settings.SetPreemptionStrategy(
  2098. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  2099. layer->SetOpacity(1.f);
  2100. // Scale down from 120% -> 100%.
  2101. settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
  2102. layer->SetTransform(gfx::Transform());
  2103. }
  2104. }
  2105. gfx::Rect CaptureModeSession::CalculateCaptureLabelWidgetBounds() {
  2106. DCHECK(capture_label_widget_);
  2107. CaptureLabelView* label_view =
  2108. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
  2109. const gfx::Size preferred_size = label_view->GetPreferredSize();
  2110. const gfx::Rect capture_bar_bounds =
  2111. capture_mode_bar_widget_->GetNativeWindow()->bounds();
  2112. // Calculates the bounds for when the capture label is not placed in the
  2113. // middle of the screen.
  2114. auto calculate_bounds = [&preferred_size, &capture_bar_bounds](
  2115. const gfx::Rect& capture_bounds,
  2116. aura::Window* root) {
  2117. // The capture_bounds must be at least the size of |preferred_size| plus
  2118. // some padding for the capture label to be centered inside it.
  2119. gfx::Rect label_bounds(capture_bounds);
  2120. gfx::Size capture_bounds_min_size = preferred_size;
  2121. capture_bounds_min_size.Enlarge(kCaptureRegionMinimumPaddingDp,
  2122. kCaptureRegionMinimumPaddingDp);
  2123. // If the label fits into |capture_bounds| with a comfortable padding, and
  2124. // does not intersect the capture bar, we're good.
  2125. if (label_bounds.width() > capture_bounds_min_size.width() &&
  2126. label_bounds.height() > capture_bounds_min_size.height()) {
  2127. label_bounds.ClampToCenteredSize(preferred_size);
  2128. if (!label_bounds.Intersects(capture_bar_bounds))
  2129. return label_bounds;
  2130. }
  2131. // The capture button may be placed along the edge of a capture region if it
  2132. // cannot be placed in the middle. This enum represents the possible edges.
  2133. enum class Direction { kBottom, kTop, kLeft, kRight };
  2134. // Try placing the label slightly outside |capture_bounds|. The label will
  2135. // be |kCaptureButtonDistanceFromRegionDp| away from |capture_bounds| along
  2136. // one of the edges. The order we will try is bottom, top, left then right.
  2137. const std::vector<Direction> directions = {
  2138. Direction::kBottom, Direction::kTop, Direction::kLeft,
  2139. Direction::kRight};
  2140. // For each direction, start off with the label in the center of
  2141. // |capture_bounds| (matching centerpoints). We will shift the label to
  2142. // slighty outside |capture_bounds| for each direction.
  2143. gfx::Rect centered_label_bounds(preferred_size);
  2144. centered_label_bounds.set_x(capture_bounds.CenterPoint().x() -
  2145. preferred_size.width() / 2);
  2146. centered_label_bounds.set_y(capture_bounds.CenterPoint().y() -
  2147. preferred_size.height() / 2);
  2148. const int spacing = kCaptureButtonDistanceFromRegionDp;
  2149. // Try the directions in the preferred order. We will early out if one of
  2150. // them is viable.
  2151. for (Direction direction : directions) {
  2152. label_bounds = centered_label_bounds;
  2153. switch (direction) {
  2154. case Direction::kBottom:
  2155. label_bounds.set_y(capture_bounds.bottom() + spacing);
  2156. break;
  2157. case Direction::kTop:
  2158. label_bounds.set_y(capture_bounds.y() - spacing -
  2159. preferred_size.height());
  2160. break;
  2161. case Direction::kLeft:
  2162. label_bounds.set_x(capture_bounds.x() - spacing -
  2163. preferred_size.width());
  2164. break;
  2165. case Direction::kRight:
  2166. label_bounds.set_x(capture_bounds.right() + spacing);
  2167. break;
  2168. }
  2169. // If |label_bounds| does not overlap with |capture_bar_bounds| and is
  2170. // fully contained in root, we're good.
  2171. if (!label_bounds.Intersects(capture_bar_bounds) &&
  2172. root->bounds().Contains(label_bounds)) {
  2173. return label_bounds;
  2174. }
  2175. }
  2176. // Reaching here, we have not found a good edge to place the label at. The
  2177. // last attempt is to place it slightly above the capture bar.
  2178. label_bounds.set_size(preferred_size);
  2179. label_bounds.set_x(capture_bar_bounds.CenterPoint().x() -
  2180. preferred_size.width() / 2);
  2181. label_bounds.set_y(capture_bar_bounds.y() -
  2182. kCaptureButtonDistanceFromRegionDp -
  2183. preferred_size.height());
  2184. return label_bounds;
  2185. };
  2186. gfx::Rect bounds(current_root_->bounds());
  2187. const gfx::Rect capture_region = controller_->user_capture_region();
  2188. const gfx::Rect window_bounds = GetSelectedWindowBounds();
  2189. const CaptureModeSource source = controller_->source();
  2190. // For fullscreen mode, the capture label is placed in the middle of the
  2191. // screen. For region capture mode, if it's in select phase, the capture label
  2192. // is also placed in the middle of the screen, and if it's in fine tune phase,
  2193. // the capture label is ideally placed in the middle of the capture region. If
  2194. // it cannot fit, then it will be placed slightly above or below the capture
  2195. // region. For window capture mode, it is the same as the region capture mode
  2196. // fine tune phase logic, in that it will first try to place the label in the
  2197. // middle of the selected window bounds, otherwise it will be placed slightly
  2198. // away from one of the edges of the selected window.
  2199. if (source == CaptureModeSource::kRegion && !is_selecting_region_ &&
  2200. !capture_region.IsEmpty()) {
  2201. if (label_view->IsInCountDownAnimation()) {
  2202. // If countdown starts, calculate the bounds based on the old capture
  2203. // label's position, otherwise, since the countdown label bounds is
  2204. // smaller than the label bounds and may fit into the capture region even
  2205. // if the old capture label doesn't fit thus was place outside of the
  2206. // capture region, it's possible that we see the countdown label animates
  2207. // to inside of the capture region from outside of the capture region.
  2208. bounds = capture_label_widget_->GetNativeWindow()->bounds();
  2209. bounds.ClampToCenteredSize(preferred_size);
  2210. } else {
  2211. bounds = calculate_bounds(capture_region, current_root_);
  2212. }
  2213. } else if (source == CaptureModeSource::kWindow && !window_bounds.IsEmpty()) {
  2214. bounds = calculate_bounds(window_bounds, current_root_);
  2215. } else {
  2216. bounds.ClampToCenteredSize(preferred_size);
  2217. }
  2218. // User capture bounds are in root window coordinates so convert them here.
  2219. wm::ConvertRectToScreen(current_root_, &bounds);
  2220. return bounds;
  2221. }
  2222. bool CaptureModeSession::ShouldCaptureLabelHandleEvent(
  2223. aura::Window* event_target) {
  2224. if (!capture_label_widget_ ||
  2225. capture_label_widget_->GetNativeWindow() != event_target) {
  2226. return false;
  2227. }
  2228. CaptureLabelView* label_view =
  2229. static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
  2230. return label_view->ShouldHandleEvent();
  2231. }
  2232. void CaptureModeSession::MaybeChangeRoot(aura::Window* new_root) {
  2233. DCHECK(new_root->IsRootWindow());
  2234. if (new_root == current_root_)
  2235. return;
  2236. auto* new_parent = GetParentContainer(new_root);
  2237. parent_container_observer_ =
  2238. std::make_unique<ParentContainerObserver>(new_parent, this);
  2239. new_parent->layer()->Add(layer());
  2240. layer()->SetBounds(new_parent->bounds());
  2241. current_root_ = new_root;
  2242. // Update the bounds of the widgets after setting the new root. For region
  2243. // capture, the capture bar will move at a later time, when the mouse is
  2244. // released.
  2245. if (controller_->source() != CaptureModeSource::kRegion)
  2246. RefreshBarWidgetBounds();
  2247. // Because we use custom cursors for region and full screen capture, we need
  2248. // to update the cursor in case the display DSF changes.
  2249. UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
  2250. /*is_touch=*/false);
  2251. // The following call to UpdateCaptureRegion will update the capture label
  2252. // bounds, moving it onto the correct display, but will early return if the
  2253. // region is already empty.
  2254. if (controller_->user_capture_region().IsEmpty())
  2255. UpdateCaptureLabelWidgetBounds(CaptureLabelAnimation::kNone);
  2256. // Start with a new region when we switch displays.
  2257. is_selecting_region_ = true;
  2258. UpdateCaptureRegion(gfx::Rect(), /*is_resizing=*/false, /*by_user=*/false);
  2259. UpdateRootWindowDimmers();
  2260. MaybeReparentCameraPreviewWidget();
  2261. }
  2262. void CaptureModeSession::UpdateRootWindowDimmers() {
  2263. root_window_dimmers_.clear();
  2264. // Add dimmers for all root windows except |current_root_| if needed.
  2265. for (aura::Window* root_window : Shell::GetAllRootWindows()) {
  2266. if (root_window == current_root_)
  2267. continue;
  2268. auto dimmer = std::make_unique<WindowDimmer>(root_window);
  2269. dimmer->window()->Show();
  2270. root_window_dimmers_.emplace(std::move(dimmer));
  2271. }
  2272. }
  2273. bool CaptureModeSession::IsUsingCustomCursor(CaptureModeType type) const {
  2274. return cursor_setter_->IsUsingCustomCursor(type);
  2275. }
  2276. void CaptureModeSession::ClampCaptureRegionToRootWindowSize() {
  2277. gfx::Rect new_capture_region = controller_->user_capture_region();
  2278. new_capture_region.AdjustToFit(current_root_->bounds());
  2279. controller_->SetUserCaptureRegion(new_capture_region, /*by_user=*/false);
  2280. }
  2281. void CaptureModeSession::EndSelection(
  2282. absl::optional<gfx::Point> cursor_screen_location) {
  2283. fine_tune_position_ = FineTunePosition::kNone;
  2284. anchor_points_.clear();
  2285. is_drag_in_progress_ = false;
  2286. Shell::Get()->UpdateCursorCompositingEnabled();
  2287. MaybeUpdateCaptureUisOpacity(cursor_screen_location);
  2288. UpdateDimensionsLabelWidget(/*is_resizing=*/false);
  2289. CloseMagnifierGlass();
  2290. }
  2291. void CaptureModeSession::RepaintRegion() {
  2292. gfx::Rect damage_region = controller_->user_capture_region();
  2293. damage_region.Inset(gfx::Insets(-kDamageInsetDp));
  2294. layer()->SchedulePaint(damage_region);
  2295. }
  2296. void CaptureModeSession::SelectDefaultRegion() {
  2297. is_selecting_region_ = false;
  2298. // Default is centered in the root, and its width and height are
  2299. // |kRegionDefaultRatio| size of the root.
  2300. gfx::Rect default_capture_region = current_root_->bounds();
  2301. default_capture_region.ClampToCenteredSize(gfx::ScaleToCeiledSize(
  2302. default_capture_region.size(), kRegionDefaultRatio));
  2303. UpdateCaptureRegion(default_capture_region, /*is_resizing=*/false,
  2304. /*by_user=*/true);
  2305. }
  2306. void CaptureModeSession::UpdateRegionHorizontally(bool left, int event_flags) {
  2307. const FineTunePosition focused_fine_tune_position =
  2308. focus_cycler_->GetFocusedFineTunePosition();
  2309. if (focused_fine_tune_position == FineTunePosition::kNone ||
  2310. focused_fine_tune_position == FineTunePosition::kTopCenter ||
  2311. focused_fine_tune_position == FineTunePosition::kBottomCenter) {
  2312. return;
  2313. }
  2314. const int change = GetArrowKeyPressChange(event_flags);
  2315. gfx::Rect new_capture_region = controller_->user_capture_region();
  2316. if (focused_fine_tune_position == FineTunePosition::kCenter) {
  2317. new_capture_region.Offset(left ? -change : change, 0);
  2318. new_capture_region.AdjustToFit(current_root_->bounds());
  2319. } else {
  2320. const gfx::Point location =
  2321. capture_mode_util::GetLocationForFineTunePosition(
  2322. new_capture_region, focused_fine_tune_position);
  2323. // If an affordance circle on the left side of the capture region is
  2324. // focused, left presses will enlarge the existing region and right presses
  2325. // will shrink the existing region. If it is on the right side, right
  2326. // presses will enlarge and left presses will shrink.
  2327. const bool affordance_on_left = location.x() == new_capture_region.x();
  2328. const bool shrink = affordance_on_left ^ left;
  2329. if (shrink && new_capture_region.width() < change)
  2330. return;
  2331. const int inset = shrink ? change : -change;
  2332. auto insets = gfx::Insets::TLBR(0, affordance_on_left ? inset : 0, 0,
  2333. affordance_on_left ? 0 : inset);
  2334. new_capture_region.Inset(insets);
  2335. ClipRectToFit(&new_capture_region, current_root_->bounds());
  2336. }
  2337. UpdateCaptureRegion(new_capture_region, /*is_resizing=*/false,
  2338. /*by_user=*/true);
  2339. }
  2340. void CaptureModeSession::UpdateRegionVertically(bool up, int event_flags) {
  2341. const FineTunePosition focused_fine_tune_position =
  2342. focus_cycler_->GetFocusedFineTunePosition();
  2343. if (focused_fine_tune_position == FineTunePosition::kNone ||
  2344. focused_fine_tune_position == FineTunePosition::kLeftCenter ||
  2345. focused_fine_tune_position == FineTunePosition::kRightCenter) {
  2346. return;
  2347. }
  2348. const int change = GetArrowKeyPressChange(event_flags);
  2349. gfx::Rect new_capture_region = controller_->user_capture_region();
  2350. // TODO(sammiequon): The below is similar to UpdateRegionHorizontally() except
  2351. // we are acting on the y-axis. Investigate if we can remove the duplication.
  2352. if (focused_fine_tune_position == FineTunePosition::kCenter) {
  2353. new_capture_region.Offset(0, up ? -change : change);
  2354. new_capture_region.AdjustToFit(current_root_->bounds());
  2355. } else {
  2356. const gfx::Point location =
  2357. capture_mode_util::GetLocationForFineTunePosition(
  2358. new_capture_region, focused_fine_tune_position);
  2359. // If an affordance circle on the top side of the capture region is
  2360. // focused, up presses will enlarge the existing region and down presses
  2361. // will shrink the existing region. If it is on the bottom side, down
  2362. // presses will enlarge and up presses will shrink.
  2363. const bool affordance_on_top = location.y() == new_capture_region.y();
  2364. const bool shrink = affordance_on_top ^ up;
  2365. if (shrink && new_capture_region.height() < change)
  2366. return;
  2367. const int inset = shrink ? change : -change;
  2368. auto insets = gfx::Insets::TLBR(affordance_on_top ? inset : 0, 0,
  2369. affordance_on_top ? 0 : inset, 0);
  2370. new_capture_region.Inset(insets);
  2371. ClipRectToFit(&new_capture_region, current_root_->bounds());
  2372. }
  2373. UpdateCaptureRegion(new_capture_region, /*is_resizing=*/false,
  2374. /*by_user=*/true);
  2375. }
  2376. void CaptureModeSession::MaybeReparentCameraPreviewWidget() {
  2377. if (!controller_->is_recording_in_progress())
  2378. controller_->camera_controller()->MaybeReparentPreviewWidget();
  2379. }
  2380. void CaptureModeSession::MaybeUpdateCameraPreviewBounds() {
  2381. if (!controller_->is_recording_in_progress()) {
  2382. controller_->camera_controller()->MaybeUpdatePreviewWidget(
  2383. /*animate=*/false);
  2384. }
  2385. }
  2386. bool CaptureModeSession::IsEventTargetedOnCaptureBar(
  2387. const ui::LocatedEvent& event) const {
  2388. DCHECK(capture_mode_bar_widget_);
  2389. auto* target = static_cast<aura::Window*>(event.target());
  2390. return capture_mode_bar_widget_->GetNativeWindow()->Contains(target);
  2391. }
  2392. bool CaptureModeSession::IsEventTargetedOnSettingsMenu(
  2393. const ui::LocatedEvent& event) const {
  2394. auto* target = static_cast<aura::Window*>(event.target());
  2395. return capture_mode_settings_widget_ &&
  2396. capture_mode_settings_widget_->GetNativeWindow()->Contains(target);
  2397. }
  2398. } // namespace ash