diff options
-rw-r--r-- | chrome/browser/ui/views/frame/browser_frame_gtk.h | 4 | ||||
-rw-r--r-- | views/widget/native_widget.h | 3 | ||||
-rw-r--r-- | views/widget/native_widget_delegate.h | 6 | ||||
-rw-r--r-- | views/widget/native_widget_gtk.cc | 163 | ||||
-rw-r--r-- | views/widget/native_widget_gtk.h | 6 | ||||
-rw-r--r-- | views/widget/native_widget_views.cc | 4 | ||||
-rw-r--r-- | views/widget/native_widget_views.h | 1 | ||||
-rw-r--r-- | views/widget/native_widget_win.cc | 427 | ||||
-rw-r--r-- | views/widget/native_widget_win.h | 32 | ||||
-rw-r--r-- | views/widget/native_widget_win_unittest.cc | 2 | ||||
-rw-r--r-- | views/widget/widget.cc | 110 | ||||
-rw-r--r-- | views/widget/widget.h | 26 | ||||
-rw-r--r-- | views/window/native_window.h | 3 | ||||
-rw-r--r-- | views/window/native_window_delegate.h | 9 | ||||
-rw-r--r-- | views/window/native_window_gtk.cc | 203 | ||||
-rw-r--r-- | views/window/native_window_gtk.h | 43 | ||||
-rw-r--r-- | views/window/native_window_win.cc | 469 | ||||
-rw-r--r-- | views/window/native_window_win.h | 61 | ||||
-rw-r--r-- | views/window/window.cc | 103 | ||||
-rw-r--r-- | views/window/window.h | 25 |
20 files changed, 769 insertions, 931 deletions
diff --git a/chrome/browser/ui/views/frame/browser_frame_gtk.h b/chrome/browser/ui/views/frame/browser_frame_gtk.h index e1f0514..d1c2014 100644 --- a/chrome/browser/ui/views/frame/browser_frame_gtk.h +++ b/chrome/browser/ui/views/frame/browser_frame_gtk.h @@ -31,9 +31,9 @@ class BrowserFrameGtk : public views::NativeWindowGtk, // Overridden from views::NativeWindowGtk: virtual gboolean OnWindowStateEvent(GtkWidget* widget, - GdkEventWindowState* event); + GdkEventWindowState* event) OVERRIDE; virtual gboolean OnConfigureEvent(GtkWidget* widget, - GdkEventConfigure* event); + GdkEventConfigure* event) OVERRIDE; private: NativeBrowserFrameDelegate* delegate_; diff --git a/views/widget/native_widget.h b/views/widget/native_widget.h index 3534f26..c410110 100644 --- a/views/widget/native_widget.h +++ b/views/widget/native_widget.h @@ -162,6 +162,9 @@ class NativeWidget { // accelerated drawing. virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0; + // Makes the NativeWindow modal. + virtual void BecomeModal() = 0; + // Widget pass-thrus, private to Views. -------------------------------------- // See method documentation in Widget. virtual gfx::Rect GetWindowScreenBounds() const = 0; diff --git a/views/widget/native_widget_delegate.h b/views/widget/native_widget_delegate.h index 2417b9e..319bc2e 100644 --- a/views/widget/native_widget_delegate.h +++ b/views/widget/native_widget_delegate.h @@ -24,6 +24,12 @@ class NativeWidgetDelegate { public: virtual ~NativeWidgetDelegate() {} + // Returns true if the window is modal. + virtual bool IsModal() const = 0; + + // Returns true if the window is a dialog box. + virtual bool IsDialogBox() const = 0; + // Returns true if the window can be activated. virtual bool CanActivate() const = 0; diff --git a/views/widget/native_widget_gtk.cc b/views/widget/native_widget_gtk.cc index 4d3c3a4..251177c 100644 --- a/views/widget/native_widget_gtk.cc +++ b/views/widget/native_widget_gtk.cc @@ -25,6 +25,7 @@ #include "ui/base/gtk/scoped_handle_gtk.h" #include "ui/base/x/x11_util.h" #include "ui/gfx/canvas_skia_paint.h" +#include "ui/gfx/gtk_util.h" #include "ui/gfx/path.h" #include "views/controls/textfield/native_textfield_views.h" #include "views/focus/view_storage.h" @@ -36,6 +37,7 @@ #include "views/widget/gtk_views_window.h" #include "views/widget/tooltip_manager_gtk.h" #include "views/widget/widget_delegate.h" +#include "views/window/hit_test.h" #include "views/window/native_window_gtk.h" #if defined(TOUCH_UI) @@ -192,6 +194,61 @@ GtkWindowType WindowTypeToGtkWindowType(Widget::InitParams::Type type) { return GTK_WINDOW_TOPLEVEL; } +// Converts a Windows-style hit test result code into a GDK window edge. +GdkWindowEdge HitTestCodeToGDKWindowEdge(int hittest_code) { + switch (hittest_code) { + case HTBOTTOM: + return GDK_WINDOW_EDGE_SOUTH; + case HTBOTTOMLEFT: + return GDK_WINDOW_EDGE_SOUTH_WEST; + case HTBOTTOMRIGHT: + case HTGROWBOX: + return GDK_WINDOW_EDGE_SOUTH_EAST; + case HTLEFT: + return GDK_WINDOW_EDGE_WEST; + case HTRIGHT: + return GDK_WINDOW_EDGE_EAST; + case HTTOP: + return GDK_WINDOW_EDGE_NORTH; + case HTTOPLEFT: + return GDK_WINDOW_EDGE_NORTH_WEST; + case HTTOPRIGHT: + return GDK_WINDOW_EDGE_NORTH_EAST; + default: + NOTREACHED(); + break; + } + // Default to something defaultish. + return HitTestCodeToGDKWindowEdge(HTGROWBOX); +} + +// Converts a Windows-style hit test result code into a GDK cursor type. +GdkCursorType HitTestCodeToGdkCursorType(int hittest_code) { + switch (hittest_code) { + case HTBOTTOM: + return GDK_BOTTOM_SIDE; + case HTBOTTOMLEFT: + return GDK_BOTTOM_LEFT_CORNER; + case HTBOTTOMRIGHT: + case HTGROWBOX: + return GDK_BOTTOM_RIGHT_CORNER; + case HTLEFT: + return GDK_LEFT_SIDE; + case HTRIGHT: + return GDK_RIGHT_SIDE; + case HTTOP: + return GDK_TOP_SIDE; + case HTTOPLEFT: + return GDK_TOP_LEFT_CORNER; + case HTTOPRIGHT: + return GDK_TOP_RIGHT_CORNER; + default: + break; + } + // Default to something defaultish. + return GDK_LEFT_PTR; +} + } // namespace // During drag and drop GTK sends a drag-leave during a drop. This means we @@ -702,6 +759,8 @@ void NativeWidgetGtk::InitNativeWidget(const Widget::InitParams& params) { G_CALLBACK(&OnMapThunk), this); g_signal_connect(widget_, "hide", G_CALLBACK(&OnHideThunk), this); + g_signal_connect(widget_, "configure-event", + G_CALLBACK(&OnConfigureEventThunk), this); // Views/FocusManager (re)sets the focus to the root window, // so we need to connect signal handlers to the gtk window. @@ -914,6 +973,10 @@ void NativeWidgetGtk::SetAccessibleRole(ui::AccessibilityTypes::Role role) { void NativeWidgetGtk::SetAccessibleState(ui::AccessibilityTypes::State state) { } +void NativeWidgetGtk::BecomeModal() { + gtk_window_set_modal(GetNativeWindow(), true); +} + gfx::Rect NativeWidgetGtk::GetWindowScreenBounds() const { // Client == Window bounds on Gtk. return GetClientAreaScreenBounds(); @@ -1207,6 +1270,20 @@ void NativeWidgetGtk::OnSizeAllocate(GtkWidget* widget, return; size_ = new_size; delegate_->OnNativeWidgetSizeChanged(size_); + + if (GetWidget()->non_client_view()) { + // The Window's NonClientView may provide a custom shape for the Window. + gfx::Path window_mask; + GetWidget()->non_client_view()->GetWindowMask(gfx::Size(allocation->width, + allocation->height), + &window_mask); + GdkRegion* mask_region = window_mask.CreateNativeRegion(); + gdk_window_shape_combine_region(GetNativeView()->window, mask_region, 0, 0); + if (mask_region) + gdk_region_destroy(mask_region); + + SaveWindowPosition(); + } } gboolean NativeWidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { @@ -1349,6 +1426,8 @@ gboolean NativeWidgetGtk::OnEnterNotify(GtkWidget* widget, gboolean NativeWidgetGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event) { + gdk_window_set_cursor(widget->window, gfx::GetCursor(GDK_LEFT_PTR)); + GetWidget()->ResetLastMouseMoveFlag(); if (!HasMouseCapture() && !GetWidget()->is_mouse_button_pressed_) { @@ -1360,6 +1439,20 @@ gboolean NativeWidgetGtk::OnLeaveNotify(GtkWidget* widget, gboolean NativeWidgetGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) { + if (GetWidget()->non_client_view()) { + GdkEventMotion transformed_event = *event; + TransformEvent(&transformed_event); + gfx::Point translated_location(transformed_event.x, transformed_event.y); + + // Update the cursor for the screen edge. + int hittest_code = + GetWidget()->non_client_view()->NonClientHitTest(translated_location); + if (hittest_code != HTCLIENT) { + GdkCursorType cursor_type = HitTestCodeToGdkCursorType(hittest_code); + gdk_window_set_cursor(widget->window, gfx::GetCursor(cursor_type)); + } + } + MouseEvent mouse_event(TransformEvent(event)); delegate_->OnMouseEvent(mouse_event); return true; @@ -1367,6 +1460,55 @@ gboolean NativeWidgetGtk::OnMotionNotify(GtkWidget* widget, gboolean NativeWidgetGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) { + if (GetWidget()->non_client_view()) { + GdkEventButton transformed_event = *event; + MouseEvent mouse_event(TransformEvent(&transformed_event)); + + int hittest_code = GetWidget()->non_client_view()->NonClientHitTest( + mouse_event.location()); + switch (hittest_code) { + case HTCAPTION: { + // Start dragging if the mouse event is a single click and *not* a right + // click. If it is a right click, then pass it through to + // NativeWidgetGtk::OnButtonPress so that View class can show + // ContextMenu upon a mouse release event. We only start drag on single + // clicks as we get a crash in Gtk on double/triple clicks. + if (event->type == GDK_BUTTON_PRESS && + !mouse_event.IsOnlyRightMouseButton()) { + gfx::Point screen_point(event->x, event->y); + View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point); + gtk_window_begin_move_drag(GetNativeWindow(), event->button, + screen_point.x(), screen_point.y(), + event->time); + return TRUE; + } + break; + } + case HTBOTTOM: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + case HTGROWBOX: + case HTLEFT: + case HTRIGHT: + case HTTOP: + case HTTOPLEFT: + case HTTOPRIGHT: { + gfx::Point screen_point(event->x, event->y); + View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point); + // TODO(beng): figure out how to get a good minimum size. + gtk_widget_set_size_request(GetNativeView(), 100, 100); + gtk_window_begin_resize_drag(GetNativeWindow(), + HitTestCodeToGDKWindowEdge(hittest_code), + event->button, screen_point.x(), + screen_point.y(), event->time); + return TRUE; + } + default: + // Everything else falls into standard client event handling... + break; + } + } + if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) { // The sequence for double clicks is press, release, press, 2press, release. // This means that at the time we get the second 'press' we don't know @@ -1508,10 +1650,20 @@ void NativeWidgetGtk::OnHide(GtkWidget* widget) { gboolean NativeWidgetGtk::OnWindowStateEvent(GtkWidget* widget, GdkEventWindowState* event) { + if (GetWidget()->non_client_view() && + !(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) { + SaveWindowPosition(); + } window_state_ = event->new_window_state; return FALSE; } +gboolean NativeWidgetGtk::OnConfigureEvent(GtkWidget* widget, + GdkEventConfigure* event) { + SaveWindowPosition(); + return FALSE; +} + void NativeWidgetGtk::HandleXGrabBroke() { } @@ -1807,6 +1959,17 @@ void NativeWidgetGtk::DrawTransparentBackground(GtkWidget* widget, cairo_destroy(cr); } +void NativeWidgetGtk::SaveWindowPosition() { + // The delegate may have gone away on us. + if (!GetWidget()->widget_delegate()) + return; + + bool maximized = window_state_ & GDK_WINDOW_STATE_MAXIMIZED; + GetWidget()->widget_delegate()->SaveWindowPlacement( + GetWidget()->GetWindowScreenBounds(), + maximized); +} + //////////////////////////////////////////////////////////////////////////////// // Widget, public: diff --git a/views/widget/native_widget_gtk.h b/views/widget/native_widget_gtk.h index f641c64..85426aa 100644 --- a/views/widget/native_widget_gtk.h +++ b/views/widget/native_widget_gtk.h @@ -188,6 +188,7 @@ class NativeWidgetGtk : public NativeWidget, virtual void SetAccessibleName(const std::wstring& name) OVERRIDE; virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE; virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE; + virtual void BecomeModal() OVERRIDE; virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE; virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE; virtual gfx::Rect GetRestoredBounds() const OVERRIDE; @@ -282,6 +283,8 @@ class NativeWidgetGtk : public NativeWidget, CHROMEGTK_CALLBACK_0(NativeWidgetGtk, void, OnHide); CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnWindowStateEvent, GdkEventWindowState*); + CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnConfigureEvent, + GdkEventConfigure*); // Invoked when the widget is destroyed and right before the object // destruction. Useful for overriding. @@ -345,6 +348,9 @@ class NativeWidgetGtk : public NativeWidget, static void DrawTransparentBackground(GtkWidget* widget, GdkEventExpose* event); + // Asks the delegate if any to save the window's location and size. + void SaveWindowPosition(); + // A delegate implementation that handles events received here. // See class documentation for Widget in widget.h for a note about ownership. internal::NativeWidgetDelegate* delegate_; diff --git a/views/widget/native_widget_views.cc b/views/widget/native_widget_views.cc index d2fa54f..f46f0e6 100644 --- a/views/widget/native_widget_views.cc +++ b/views/widget/native_widget_views.cc @@ -168,6 +168,10 @@ void NativeWidgetViews::SetAccessibleState( ui::AccessibilityTypes::State state) { } +void NativeWidgetViews::BecomeModal() { + NOTIMPLEMENTED(); +} + gfx::AcceleratedWidget NativeWidgetViews::GetAcceleratedWidget() { // TODO(sky): return gfx::kNullAcceleratedWidget; diff --git a/views/widget/native_widget_views.h b/views/widget/native_widget_views.h index f8ab599..cbc910a 100644 --- a/views/widget/native_widget_views.h +++ b/views/widget/native_widget_views.h @@ -69,6 +69,7 @@ class NativeWidgetViews : public NativeWidget { virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE; virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE; virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; + virtual void BecomeModal() OVERRIDE; virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE; virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE; virtual gfx::Rect GetRestoredBounds() const OVERRIDE; diff --git a/views/widget/native_widget_win.cc b/views/widget/native_widget_win.cc index 6d77bc6..599bdd5 100644 --- a/views/widget/native_widget_win.cc +++ b/views/widget/native_widget_win.cc @@ -5,9 +5,12 @@ #include "views/widget/native_widget_win.h" #include <dwmapi.h> +#include <shellapi.h> #include "base/string_util.h" #include "base/system_monitor/system_monitor.h" +#include "base/win/scoped_gdi_object.h" +#include "base/win/win_util.h" #include "base/win/windows_version.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_source.h" @@ -19,6 +22,7 @@ #include "ui/base/view_prop.h" #include "ui/base/win/hwnd_util.h" #include "ui/gfx/canvas_skia.h" +#include "ui/gfx/canvas_skia_paint.h" #include "ui/gfx/icon_util.h" #include "ui/gfx/native_theme_win.h" #include "ui/gfx/path.h" @@ -199,6 +203,68 @@ BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) { return TRUE; } +// See comments in OnNCPaint() for details of this struct. +struct ClipState { + // The window being painted. + HWND parent; + + // DC painting to. + HDC dc; + + // Origin of the window in terms of the screen. + int x; + int y; +}; + +// See comments in OnNCPaint() for details of this function. +static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { + ClipState* clip_state = reinterpret_cast<ClipState*>(param); + if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { + RECT bounds; + GetWindowRect(window, &bounds); + ExcludeClipRect(clip_state->dc, + bounds.left - clip_state->x, + bounds.top - clip_state->y, + bounds.right - clip_state->x, + bounds.bottom - clip_state->y); + } + return TRUE; +} + +// The thickness of an auto-hide taskbar in pixels. +static const int kAutoHideTaskbarThicknessPx = 2; + +bool GetMonitorAndRects(const RECT& rect, + HMONITOR* monitor, + gfx::Rect* monitor_rect, + gfx::Rect* work_area) { + DCHECK(monitor); + DCHECK(monitor_rect); + DCHECK(work_area); + *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); + if (!*monitor) + return false; + MONITORINFO monitor_info = { 0 }; + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfo(*monitor, &monitor_info); + *monitor_rect = monitor_info.rcMonitor; + *work_area = monitor_info.rcWork; + return true; +} + +// Returns true if edge |edge| (one of ABE_LEFT, TOP, RIGHT, or BOTTOM) of +// monitor |monitor| has an auto-hiding taskbar that's always-on-top. +bool EdgeHasTopmostAutoHideTaskbar(UINT edge, HMONITOR monitor) { + APPBARDATA taskbar_data = { 0 }; + taskbar_data.cbSize = sizeof APPBARDATA; + taskbar_data.uEdge = edge; + HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, + &taskbar_data)); + return ::IsWindow(taskbar) && (monitor != NULL) && + (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && + (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST); +} + // Links the HWND to its NativeWidget. const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__"; @@ -280,7 +346,12 @@ NativeWidgetWin::NativeWidgetWin(internal::NativeWidgetDelegate* delegate) fullscreen_(false), force_hidden_count_(0), lock_updates_(false), - saved_window_style_(0) { + saved_window_style_(0), + ignore_window_pos_changes_(false), + ignore_pos_changes_factory_(this), + last_monitor_(NULL), + is_right_mouse_pressed_on_caption_(false), + restored_enabled_(false) { } NativeWidgetWin::~NativeWidgetWin() { @@ -300,6 +371,15 @@ bool NativeWidgetWin::IsAeroGlassEnabled() { return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; } +// static +gfx::Font NativeWidgetWin::GetWindowTitleFont() { + NONCLIENTMETRICS ncm; + base::win::GetNonClientMetrics(&ncm); + l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); + base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); + return gfx::Font(caption_font); +} + void NativeWidgetWin::Show(int show_state) { ShowWindow(show_state); // When launched from certain programs like bash and Windows Live Messenger, @@ -366,6 +446,9 @@ void NativeWidgetWin::PopForceHidden() { void NativeWidgetWin::InitNativeWidget(const Widget::InitParams& params) { SetInitParams(params); + GetMonitorAndRects(params.bounds.ToRECT(), &last_monitor_, + &last_monitor_rect_, &last_work_area_); + // Create the window. gfx::NativeView parent = params.parent_widget ? params.parent_widget->GetNativeView() : params.parent; @@ -617,6 +700,17 @@ void NativeWidgetWin::SetAccessibleState(ui::AccessibilityTypes::State state) { } } +void NativeWidgetWin::BecomeModal() { + // We implement modality by crawling up the hierarchy of windows starting + // at the owner, disabling all of them so that they don't receive input + // messages. + HWND start = ::GetWindow(GetNativeView(), GW_OWNER); + while (start) { + ::EnableWindow(start, FALSE); + start = ::GetParent(start); + } +} + gfx::Rect NativeWidgetWin::GetWindowScreenBounds() const { RECT r; GetWindowRect(&r); @@ -1097,6 +1191,7 @@ LRESULT NativeWidgetWin::OnCreate(CREATESTRUCT* create_struct) { } void NativeWidgetWin::OnDestroy() { + RestoreEnabledIfNecessary(); delegate_->OnNativeWidgetDestroying(); if (drop_target_.get()) { RevokeDragDrop(hwnd()); @@ -1309,6 +1404,75 @@ LRESULT NativeWidgetWin::OnMouseActivate(UINT message, LRESULT NativeWidgetWin::OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param) { + if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { + is_right_mouse_pressed_on_caption_ = false; + ReleaseCapture(); + // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() + // expect screen coordinates. + CPoint screen_point(l_param); + MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_point, 1); + w_param = SendMessage(GetNativeView(), WM_NCHITTEST, 0, + MAKELPARAM(screen_point.x, screen_point.y)); + if (w_param == HTCAPTION || w_param == HTSYSMENU) { + UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD; + if (base::i18n::IsRTL()) + flags |= TPM_RIGHTALIGN; + HMENU system_menu = GetSystemMenu(GetNativeView(), FALSE); + int id = TrackPopupMenu(system_menu, flags, screen_point.x, + screen_point.y, 0, GetNativeView(), NULL); + ExecuteSystemMenuCommand(id); + return 0; + } + } else if (message == WM_NCLBUTTONDOWN && + !GetWidget()->ShouldUseNativeFrame()) { + switch (w_param) { + case HTCLOSE: + case HTMINBUTTON: + case HTMAXBUTTON: { + // When the mouse is pressed down in these specific non-client areas, + // we need to tell the RootView to send the mouse pressed event (which + // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ + // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be + // sent by the applicable button's ButtonListener. We _have_ to do this + // way rather than letting Windows just send the syscommand itself (as + // would happen if we never did this dance) because for some insane + // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed + // window control button appearance, in the Windows classic style, over + // our view! Ick! By handling this message we prevent Windows from + // doing this undesirable thing, but that means we need to roll the + // sys-command handling ourselves. + // Combine |w_param| with common key state message flags. + w_param |= ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)? MK_CONTROL : 0; + w_param |= ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)? MK_SHIFT : 0; + } + } + } else if (message == WM_NCRBUTTONDOWN && + (w_param == HTCAPTION || w_param == HTSYSMENU)) { + is_right_mouse_pressed_on_caption_ = true; + // We SetMouseCapture() to ensure we only show the menu when the button + // down and up are both on the caption. Note: this causes the button up to + // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. + SetMouseCapture(); + } + + /* + TODO(beng): This fixes some situations where the windows-classic appearance + non-client area is rendered over our custom frame, however it + causes mouse-releases to the non-client area to be eaten, so it + can't be enabled. + if (message == WM_NCLBUTTONDOWN) { + // NativeWidgetWin::OnNCLButtonDown set the message as un-handled. This + // normally means NativeWidgetWin::ProcessWindowMessage will pass it to + // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird + // non-client painting, so we need to call it directly here inside a + // scoped update lock. + ScopedRedrawLock lock(this); + NativeWidgetWin::OnMouseRange(message, w_param, l_param); + DefWindowProc(GetNativeView(), WM_NCLBUTTONDOWN, w_param, l_param); + SetMsgHandled(TRUE); + } + */ + MSG msg = { hwnd(), message, w_param, l_param, 0, { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } }; MouseEvent event(msg); @@ -1385,9 +1549,86 @@ LRESULT NativeWidgetWin::OnNCActivate(BOOL active) { return CallDefaultNCActivateHandler(inactive_rendering_disabled || active); } -LRESULT NativeWidgetWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) { - SetMsgHandled(FALSE); - return 0; +LRESULT NativeWidgetWin::OnNCCalcSize(BOOL mode, LPARAM l_param) { + // We only override the default handling if we need to specify a custom + // non-client edge width. Note that in most cases "no insets" means no + // custom width, but in fullscreen mode we want a custom width of 0. + gfx::Insets insets = GetClientAreaInsets(); + if (insets.empty() && !IsFullscreen()) { + SetMsgHandled(FALSE); + return 0; + } + + RECT* client_rect = mode ? + &reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0] : + reinterpret_cast<RECT*>(l_param); + client_rect->left += insets.left(); + client_rect->top += insets.top(); + client_rect->bottom -= insets.bottom(); + client_rect->right -= insets.right(); + if (IsMaximized()) { + // Find all auto-hide taskbars along the screen edges and adjust in by the + // thickness of the auto-hide taskbar on each such edge, so the window isn't + // treated as a "fullscreen app", which would cause the taskbars to + // disappear. + HMONITOR monitor = MonitorFromWindow(GetNativeView(), + MONITOR_DEFAULTTONULL); + if (!monitor) { + // We might end up here if the window was previously minimized and the + // user clicks on the taskbar button to restore it in the previously + // maximized position. In that case WM_NCCALCSIZE is sent before the + // window coordinates are restored to their previous values, so our + // (left,top) would probably be (-32000,-32000) like all minimized + // windows. So the above MonitorFromWindow call fails, but if we check + // the window rect given with WM_NCCALCSIZE (which is our previous + // restored window position) we will get the correct monitor handle. + monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); + if (!monitor) { + // This is probably an extreme case that we won't hit, but if we don't + // intersect any monitor, let us not adjust the client rect since our + // window will not be visible anyway. + return 0; + } + } + if (EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor)) + client_rect->left += kAutoHideTaskbarThicknessPx; + if (EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor)) { + if (GetWidget()->ShouldUseNativeFrame()) { + // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of + // WM_NCHITTEST, having any nonclient area atop the window causes the + // caption buttons to draw onscreen but not respond to mouse + // hover/clicks. + // So for a taskbar at the screen top, we can't push the + // client_rect->top down; instead, we move the bottom up by one pixel, + // which is the smallest change we can make and still get a client area + // less than the screen size. This is visibly ugly, but there seems to + // be no better solution. + --client_rect->bottom; + } else { + client_rect->top += kAutoHideTaskbarThicknessPx; + } + } + if (EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor)) + client_rect->right -= kAutoHideTaskbarThicknessPx; + if (EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor)) + client_rect->bottom -= kAutoHideTaskbarThicknessPx; + + // We cannot return WVR_REDRAW when there is nonclient area, or Windows + // exhibits bugs where client pixels and child HWNDs are mispositioned by + // the width/height of the upper-left nonclient area. + return 0; + } + + // If the window bounds change, we're going to relayout and repaint anyway. + // Returning WVR_REDRAW avoids an extra paint before that of the old client + // pixels in the (now wrong) location, and thus makes actions like resizing a + // window from the left edge look slightly less broken. + // We special case when left or top insets are 0, since these conditions + // actually require another repaint to correct the layout after glass gets + // turned on and off. + if (insets.left() == 0 || insets.top() == 0) + return 0; + return mode ? WVR_REDRAW : 0; } LRESULT NativeWidgetWin::OnNCHitTest(const CPoint& point) { @@ -1421,7 +1662,85 @@ LRESULT NativeWidgetWin::OnNCHitTest(const CPoint& point) { } void NativeWidgetWin::OnNCPaint(HRGN rgn) { - SetMsgHandled(FALSE); + // We only do non-client painting if we're not using the native frame. + // It's required to avoid some native painting artifacts from appearing when + // the window is resized. + if (!GetWidget()->non_client_view() || GetWidget()->ShouldUseNativeFrame()) { + SetMsgHandled(FALSE); + return; + } + + // We have an NC region and need to paint it. We expand the NC region to + // include the dirty region of the root view. This is done to minimize + // paints. + CRect window_rect; + GetWindowRect(&window_rect); + + if (window_rect.Width() != GetWidget()->GetRootView()->width() || + window_rect.Height() != GetWidget()->GetRootView()->height()) { + // If the size of the window differs from the size of the root view it + // means we're being asked to paint before we've gotten a WM_SIZE. This can + // happen when the user is interactively resizing the window. To avoid + // mass flickering we don't do anything here. Once we get the WM_SIZE we'll + // reset the region of the window which triggers another WM_NCPAINT and + // all is well. + return; + } + + CRect dirty_region; + // A value of 1 indicates paint all. + if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { + dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); + } else { + RECT rgn_bounding_box; + GetRgnBox(rgn, &rgn_bounding_box); + if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) + return; // Dirty region doesn't intersect window bounds, bale. + + // rgn_bounding_box is in screen coordinates. Map it to window coordinates. + OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); + } + + // In theory GetDCEx should do what we want, but I couldn't get it to work. + // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell + // it doesn't work at all. So, instead we get the DC for the window then + // manually clip out the children. + HDC dc = GetWindowDC(GetNativeView()); + ClipState clip_state; + clip_state.x = window_rect.left; + clip_state.y = window_rect.top; + clip_state.parent = GetNativeView(); + clip_state.dc = dc; + EnumChildWindows(GetNativeView(), &ClipDCToChild, + reinterpret_cast<LPARAM>(&clip_state)); + + gfx::Rect old_paint_region = invalid_rect(); + + if (!old_paint_region.IsEmpty()) { + // The root view has a region that needs to be painted. Include it in the + // region we're going to paint. + + CRect old_paint_region_crect = old_paint_region.ToRECT(); + CRect tmp = dirty_region; + UnionRect(&dirty_region, &tmp, &old_paint_region_crect); + } + + GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region)); + + // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap + // the following in a block to force paint to occur so that we can release + // the dc. + { + gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, + dirty_region.top, dirty_region.Width(), + dirty_region.Height()); + delegate_->OnNativeWidgetPaint(&canvas); + } + + ReleaseDC(GetNativeView(), dc); + // When using a custom frame, we want to avoid calling DefWindowProc() since + // that may render artifacts. + SetMsgHandled(!GetWidget()->ShouldUseNativeFrame()); } LRESULT NativeWidgetWin::OnNCUAHDrawCaption(UINT msg, @@ -1600,6 +1919,67 @@ void NativeWidgetWin::OnVScroll(int scroll_type, } void NativeWidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) { + if (ignore_window_pos_changes_) { + // If somebody's trying to toggle our visibility, change the nonclient area, + // change our Z-order, or activate us, we should probably let it go through. + if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | + SWP_FRAMECHANGED)) && + (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { + // Just sizing/moving the window; ignore. + window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; + window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); + } + } else if (!GetParent()) { + CRect window_rect; + HMONITOR monitor; + gfx::Rect monitor_rect, work_area; + if (GetWindowRect(&window_rect) && + GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { + if (monitor && (monitor == last_monitor_) && + (IsFullscreen() || ((monitor_rect == last_monitor_rect_) && + (work_area != last_work_area_)))) { + // A rect for the monitor we're on changed. Normally Windows notifies + // us about this (and thus we're reaching here due to the SetWindowPos() + // call in OnSettingChange() above), but with some software (e.g. + // nVidia's nView desktop manager) the work area can change asynchronous + // to any notification, and we're just sent a SetWindowPos() call with a + // new (frequently incorrect) position/size. In either case, the best + // response is to throw away the existing position/size information in + // |window_pos| and recalculate it based on the new work rect. + gfx::Rect new_window_rect; + if (IsFullscreen()) { + new_window_rect = monitor_rect; + } else if (IsZoomed()) { + new_window_rect = work_area; + int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); + new_window_rect.Inset(-border_thickness, -border_thickness); + } else { + new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area); + } + window_pos->x = new_window_rect.x(); + window_pos->y = new_window_rect.y(); + window_pos->cx = new_window_rect.width(); + window_pos->cy = new_window_rect.height(); + // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child + // HWNDs for some reason. + window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); + window_pos->flags |= SWP_NOCOPYBITS; + + // Now ignore all immediately-following SetWindowPos() changes. Windows + // likes to (incorrectly) recalculate what our position/size should be + // and send us further updates. + ignore_window_pos_changes_ = true; + DCHECK(ignore_pos_changes_factory_.empty()); + MessageLoop::current()->PostTask(FROM_HERE, + ignore_pos_changes_factory_.NewRunnableMethod( + &NativeWidgetWin::StopIgnoringPosChanges)); + } + last_monitor_ = monitor; + last_monitor_rect_ = monitor_rect; + last_work_area_ = work_area; + } + } + if (force_hidden_count_) { // Prevent the window from being made visible if we've been asked to do so. // See comment in header as to why we might want this. @@ -1631,7 +2011,7 @@ int NativeWidgetWin::GetShowState() const { gfx::Insets NativeWidgetWin::GetClientAreaInsets() const { // Returning an empty Insets object causes the default handling in // NativeWidgetWin::OnNCCalcSize() to be invoked. - if (GetWidget()->ShouldUseNativeFrame()) + if (!GetWidget()->non_client_view() || GetWidget()->ShouldUseNativeFrame()) return gfx::Insets(); if (IsMaximized()) { @@ -1780,7 +2160,26 @@ void NativeWidgetWin::SetInitParams(const Widget::InitParams& params) { // Set type-dependent style attributes. switch (params.type) { - case Widget::InitParams::TYPE_WINDOW: + case Widget::InitParams::TYPE_WINDOW: { + style |= WS_SYSMENU | WS_CAPTION; + bool can_resize = GetWidget()->widget_delegate()->CanResize(); + bool can_maximize = GetWidget()->widget_delegate()->CanMaximize(); + if (can_maximize) { + style |= WS_OVERLAPPEDWINDOW; + } else if (can_resize) { + style |= WS_OVERLAPPED | WS_THICKFRAME; + } + if (delegate_->IsDialogBox()) { + style |= DS_MODALFRAME; + // NOTE: Turning this off means we lose the close button, which is bad. + // Turning it on though means the user can maximize or size the window + // from the system menu, which is worse. We may need to provide our own + // menu to get the close button to appear properly. + // style &= ~WS_SYSMENU; + } + ex_style |= delegate_->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0; + break; + } case Widget::InitParams::TYPE_CONTROL: break; case Widget::InitParams::TYPE_WINDOW_FRAMELESS: @@ -1924,6 +2323,20 @@ gfx::AcceleratedWidget NativeWidgetWin::GetAcceleratedWidget() { #endif } +void NativeWidgetWin::RestoreEnabledIfNecessary() { + if (delegate_->IsModal() && !restored_enabled_) { + restored_enabled_ = true; + // If we were run modally, we need to undo the disabled-ness we inflicted on + // the owner's parent hierarchy. + HWND start = ::GetWindow(GetNativeView(), GW_OWNER); + while (start) { + ::EnableWindow(start, TRUE); + start = ::GetParent(start); + } + } +} + + void NativeWidgetWin::DispatchKeyEventPostIME(const KeyEvent& key) { SetMsgHandled(delegate_->OnKeyEvent(key)); } diff --git a/views/widget/native_widget_win.h b/views/widget/native_widget_win.h index 3d98b24..94bf6e2 100644 --- a/views/widget/native_widget_win.h +++ b/views/widget/native_widget_win.h @@ -30,6 +30,7 @@ class ViewProp; namespace gfx { class CanvasSkia; +class Font; class Rect; } @@ -88,6 +89,9 @@ class NativeWidgetWin : public ui::WindowImpl, // enabled. static bool IsAeroGlassEnabled(); + // Returns the system set window title font. + static gfx::Font GetWindowTitleFont(); + // Show the window with the specified show command. void Show(int show_state); @@ -212,6 +216,7 @@ class NativeWidgetWin : public ui::WindowImpl, virtual void SetAccessibleName(const std::wstring& name) OVERRIDE; virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE; virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE; + virtual void BecomeModal() OVERRIDE; virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE; virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE; virtual gfx::Rect GetRestoredBounds() const OVERRIDE; @@ -513,6 +518,11 @@ class NativeWidgetWin : public ui::WindowImpl, // flicker. LRESULT CallDefaultNCActivateHandler(BOOL active); + // Stops ignoring SetWindowPos() requests (see below). + void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; } + + void RestoreEnabledIfNecessary(); + // Overridden from NativeWidget. virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; @@ -625,6 +635,28 @@ class NativeWidgetWin : public ui::WindowImpl, // The window styles of the window before updates were locked. DWORD saved_window_style_; + // When true, this flag makes us discard incoming SetWindowPos() requests that + // only change our position/size. (We still allow changes to Z-order, + // activation, etc.) + bool ignore_window_pos_changes_; + + // The following factory is used to ignore SetWindowPos() calls for short time + // periods. + ScopedRunnableMethodFactory<NativeWidgetWin> ignore_pos_changes_factory_; + + // The last-seen monitor containing us, and its rect and work area. These are + // used to catch updates to the rect and work area and react accordingly. + HMONITOR last_monitor_; + gfx::Rect last_monitor_rect_, last_work_area_; + + // Set to true when the user presses the right mouse button on the caption + // area. We need this so we can correctly show the context menu on mouse-up. + bool is_right_mouse_pressed_on_caption_; + + // Whether all ancestors have been enabled. This is only used if is_modal_ is + // true. + bool restored_enabled_; + DISALLOW_COPY_AND_ASSIGN(NativeWidgetWin); }; diff --git a/views/widget/native_widget_win_unittest.cc b/views/widget/native_widget_win_unittest.cc index d4345a8..f2465f4 100644 --- a/views/widget/native_widget_win_unittest.cc +++ b/views/widget/native_widget_win_unittest.cc @@ -44,7 +44,7 @@ class NativeWidgetWinTest : public testing::Test { NativeWidgetWin* NativeWidgetWinTest::CreateNativeWidgetWin() { scoped_ptr<Widget> widget(new Widget); - Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(50, 50, 650, 650); widget->Init(params); diff --git a/views/widget/widget.cc b/views/widget/widget.cc index 5dbfe44..94eabd5 100644 --- a/views/widget/widget.cc +++ b/views/widget/widget.cc @@ -7,6 +7,8 @@ #include "base/logging.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" +#include "ui/base/l10n/l10n_font_util.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/compositor/compositor.h" #include "views/focus/view_storage.h" #include "views/ime/input_method.h" @@ -98,7 +100,9 @@ Widget::Widget() is_secondary_widget_(true), frame_type_(FRAME_TYPE_DEFAULT), disable_inactive_rendering_(false), - widget_closed_(false) { + widget_closed_(false), + saved_maximized_state_(false), + minimum_size_(100, 100) { } Widget::~Widget() { @@ -125,6 +129,25 @@ Widget* Widget::GetWidgetForNativeView(gfx::NativeView native_view) { return native_widget ? native_widget->GetWidget() : NULL; } +// static +int Widget::GetLocalizedContentsWidth(int col_resource_id) { + return ui::GetLocalizedContentsWidthForFont(col_resource_id, + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); +} + +// static +int Widget::GetLocalizedContentsHeight(int row_resource_id) { + return ui::GetLocalizedContentsHeightForFont(row_resource_id, + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); +} + +// static +gfx::Size Widget::GetLocalizedContentsSize(int col_resource_id, + int row_resource_id) { + return gfx::Size(GetLocalizedContentsWidth(col_resource_id), + GetLocalizedContentsHeight(row_resource_id)); +} + void Widget::Init(const InitParams& params) { widget_delegate_ = params.delegate ? params.delegate : new DefaultWidgetDelegate; @@ -144,6 +167,8 @@ void Widget::Init(const InitParams& params) { // NonClientView to the RootView. This will cause everything to be parented. non_client_view_->set_client_view(widget_delegate_->CreateClientView(this)); SetContentsView(non_client_view_); + SetInitialBounds(params.bounds); + UpdateWindowTitle(); } } @@ -284,7 +309,17 @@ void Widget::EnableClose(bool enable) { } void Widget::Show() { - native_widget_->Show(); + if (non_client_view_) { + native_widget_->ShowNativeWidget( + saved_maximized_state_ ? NativeWidget::SHOW_MAXIMIZED + : NativeWidget::SHOW_RESTORED); + // |saved_maximized_state_| only applies the first time the window is shown. + // If we don't reset the value the window will be shown maximized every time + // it is subsequently shown after being hidden. + saved_maximized_state_ = false; + } else { + native_widget_->Show(); + } } void Widget::Hide() { @@ -433,11 +468,12 @@ void Widget::ResetLastMouseMoveFlag() { } void Widget::UpdateWindowTitle() { - if (non_client_view_) { - // If the non-client view is rendering its own title, it'll need to relayout - // now. - non_client_view_->Layout(); - } + if (!non_client_view_) + return; + + // If the non-client view is rendering its own title, it'll need to relayout + // now. + non_client_view_->Layout(); // Update the native frame's text. We do this regardless of whether or not // the native frame is being used, since this also updates the taskbar, etc. @@ -524,6 +560,14 @@ void Widget::NotifyAccessibilityEvent( //////////////////////////////////////////////////////////////////////////////// // Widget, NativeWidgetDelegate implementation: +bool Widget::IsModal() const { + return widget_delegate_->IsModal(); +} + +bool Widget::IsDialogBox() const { + return !!widget_delegate_->AsDialogDelegate(); +} + bool Widget::CanActivate() const { return widget_delegate_->CanActivate(); } @@ -570,6 +614,9 @@ void Widget::OnNativeWidgetCreated() { widget_delegate_->GetAccessibleWindowRole()); native_widget_->SetAccessibleState( widget_delegate_->GetAccessibleWindowState()); + + if (widget_delegate_->IsModal()) + native_widget_->BecomeModal(); } void Widget::OnNativeWidgetDestroying() { @@ -780,6 +827,55 @@ void Widget::SaveWindowPosition() { widget_delegate_->SaveWindowPlacement(bounds, maximized); } +void Widget::SetInitialBounds(const gfx::Rect& bounds) { + if (!non_client_view_) + return; + + // First we obtain the window's saved show-style and store it. We need to do + // this here, rather than in Show() because by the time Show() is called, + // the window's size will have been reset (below) and the saved maximized + // state will have been lost. Sadly there's no way to tell on Windows when + // a window is restored from maximized state, so we can't more accurately + // track maximized state independently of sizing information. + widget_delegate_->GetSavedMaximizedState( + &saved_maximized_state_); + + // Restore the window's placement from the controller. + gfx::Rect saved_bounds = bounds; + if (widget_delegate_->GetSavedWindowBounds(&saved_bounds)) { + if (!widget_delegate_->ShouldRestoreWindowSize()) { + saved_bounds.set_size(non_client_view_->GetPreferredSize()); + } else { + // Make sure the bounds are at least the minimum size. + if (saved_bounds.width() < minimum_size_.width()) { + saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(), + saved_bounds.right() + minimum_size_.width() - + saved_bounds.width(), + saved_bounds.bottom()); + } + + if (saved_bounds.height() < minimum_size_.height()) { + saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(), + saved_bounds.right(), + saved_bounds.bottom() + minimum_size_.height() - + saved_bounds.height()); + } + } + + // Widget's SetBounds method does not further modify the bounds that are + // passed to it. + SetBounds(saved_bounds); + } else { + if (bounds.IsEmpty()) { + // No initial bounds supplied, so size the window to its content and + // center over its parent. + native_widget_->CenterWindow(non_client_view_->GetPreferredSize()); + } else { + // Use the supplied initial bounds. + SetBoundsConstrained(bounds, NULL); + } + } +} } // namespace views diff --git a/views/widget/widget.h b/views/widget/widget.h index 990d060..9916d95c 100644 --- a/views/widget/widget.h +++ b/views/widget/widget.h @@ -93,7 +93,8 @@ class Widget : public internal::NativeWidgetDelegate, struct InitParams { enum Type { - TYPE_WINDOW, // A Window, like a frame window. + TYPE_WINDOW, // A decorated Window, like a frame window. + // Widgets of TYPE_WINDOW will have a NonClientView. TYPE_WINDOW_FRAMELESS, // An undecorated Window. TYPE_CONTROL, // A control, like a button. @@ -166,6 +167,17 @@ class Widget : public internal::NativeWidgetDelegate, // Passes through to NativeWidget::GetWidgetForNativeView(). static Widget* GetWidgetForNativeView(gfx::NativeView native_view); + + // Returns the preferred size of the contents view of this window based on + // its localized size data. The width in cols is held in a localized string + // resource identified by |col_resource_id|, the height in the same fashion. + // TODO(beng): This should eventually live somewhere else, probably closer to + // ClientView. + static int GetLocalizedContentsWidth(int col_resource_id); + static int GetLocalizedContentsHeight(int row_resource_id); + static gfx::Size GetLocalizedContentsSize(int col_resource_id, + int row_resource_id); + void Init(const InitParams& params); // Unconverted methods ------------------------------------------------------- @@ -447,6 +459,8 @@ class Widget : public internal::NativeWidgetDelegate, virtual const Window* AsWindow() const; // Overridden from NativeWidgetDelegate: + virtual bool IsModal() const OVERRIDE; + virtual bool IsDialogBox() const OVERRIDE; virtual bool CanActivate() const OVERRIDE; virtual bool IsInactiveRenderingDisabled() const OVERRIDE; virtual void EnableInactiveRendering() OVERRIDE; @@ -518,6 +532,9 @@ class Widget : public internal::NativeWidgetDelegate, // window delegate. void SaveWindowPosition(); + // Sizes and positions the window just after it is created. + void SetInitialBounds(const gfx::Rect& bounds); + NativeWidget* native_widget_; // Non-owned pointer to the Widget's delegate. May be NULL if no delegate is @@ -568,6 +585,13 @@ class Widget : public internal::NativeWidgetDelegate, // Set to true if the widget is in the process of closing. bool widget_closed_; + // The saved maximized state for this window. See note in SetInitialBounds + // that explains why we save this. + bool saved_maximized_state_; + + // The smallest size the window can be. + gfx::Size minimum_size_; + DISALLOW_COPY_AND_ASSIGN(Widget); }; diff --git a/views/window/native_window.h b/views/window/native_window.h index 278a54f..de5cce8 100644 --- a/views/window/native_window.h +++ b/views/window/native_window.h @@ -44,9 +44,6 @@ class NativeWindow { protected: friend class Window; - - // Makes the NativeWindow modal. - virtual void BecomeModal() = 0; }; } // namespace views diff --git a/views/window/native_window_delegate.h b/views/window/native_window_delegate.h index a317f7d..41621ac 100644 --- a/views/window/native_window_delegate.h +++ b/views/window/native_window_delegate.h @@ -23,15 +23,6 @@ class NativeWindowDelegate { public: virtual ~NativeWindowDelegate() {} - // Returns true if the window is modal. - virtual bool IsModal() const = 0; - - // Returns true if the window is a dialog box. - virtual bool IsDialogBox() const = 0; - - // Called just after the NativeWindow has been created. - virtual void OnNativeWindowCreated(const gfx::Rect& bounds) = 0; - // virtual Window* AsWindow() = 0; diff --git a/views/window/native_window_gtk.cc b/views/window/native_window_gtk.cc index 99a4f8a..2ca707a 100644 --- a/views/window/native_window_gtk.cc +++ b/views/window/native_window_gtk.cc @@ -6,7 +6,6 @@ #include "base/i18n/rtl.h" #include "base/utf_string_conversions.h" -#include "ui/gfx/gtk_util.h" #include "ui/gfx/path.h" #include "ui/gfx/rect.h" #include "views/events/event.h" @@ -15,71 +14,11 @@ #include "views/window/non_client_view.h" #include "views/window/window_delegate.h" -namespace { - -// Converts a Windows-style hit test result code into a GDK window edge. -GdkWindowEdge HitTestCodeToGDKWindowEdge(int hittest_code) { - switch (hittest_code) { - case HTBOTTOM: - return GDK_WINDOW_EDGE_SOUTH; - case HTBOTTOMLEFT: - return GDK_WINDOW_EDGE_SOUTH_WEST; - case HTBOTTOMRIGHT: - case HTGROWBOX: - return GDK_WINDOW_EDGE_SOUTH_EAST; - case HTLEFT: - return GDK_WINDOW_EDGE_WEST; - case HTRIGHT: - return GDK_WINDOW_EDGE_EAST; - case HTTOP: - return GDK_WINDOW_EDGE_NORTH; - case HTTOPLEFT: - return GDK_WINDOW_EDGE_NORTH_WEST; - case HTTOPRIGHT: - return GDK_WINDOW_EDGE_NORTH_EAST; - default: - NOTREACHED(); - break; - } - // Default to something defaultish. - return HitTestCodeToGDKWindowEdge(HTGROWBOX); -} - -// Converts a Windows-style hit test result code into a GDK cursor type. -GdkCursorType HitTestCodeToGdkCursorType(int hittest_code) { - switch (hittest_code) { - case HTBOTTOM: - return GDK_BOTTOM_SIDE; - case HTBOTTOMLEFT: - return GDK_BOTTOM_LEFT_CORNER; - case HTBOTTOMRIGHT: - case HTGROWBOX: - return GDK_BOTTOM_RIGHT_CORNER; - case HTLEFT: - return GDK_LEFT_SIDE; - case HTRIGHT: - return GDK_RIGHT_SIDE; - case HTTOP: - return GDK_TOP_SIDE; - case HTTOPLEFT: - return GDK_TOP_LEFT_CORNER; - case HTTOPRIGHT: - return GDK_TOP_RIGHT_CORNER; - default: - break; - } - // Default to something defaultish. - return GDK_LEFT_PTR; -} - -} // namespace - namespace views { NativeWindowGtk::NativeWindowGtk(internal::NativeWindowDelegate* delegate) : NativeWidgetGtk(delegate->AsNativeWidgetDelegate()), - delegate_(delegate), - window_closed_(false) { + delegate_(delegate) { is_window_ = true; } @@ -87,114 +26,6 @@ NativeWindowGtk::~NativeWindowGtk() { } //////////////////////////////////////////////////////////////////////////////// -// NativeWindowGtk, NativeWidgetGtk overrides: - -gboolean NativeWindowGtk::OnButtonPress(GtkWidget* widget, - GdkEventButton* event) { - GdkEventButton transformed_event = *event; - MouseEvent mouse_event(TransformEvent(&transformed_event)); - - int hittest_code = - GetWindow()->non_client_view()->NonClientHitTest(mouse_event.location()); - switch (hittest_code) { - case HTCAPTION: { - // Start dragging if the mouse event is a single click and *not* a right - // click. If it is a right click, then pass it through to - // NativeWidgetGtk::OnButtonPress so that View class can show ContextMenu - // upon a mouse release event. We only start drag on single clicks as we - // get a crash in Gtk on double/triple clicks. - if (event->type == GDK_BUTTON_PRESS && - !mouse_event.IsOnlyRightMouseButton()) { - gfx::Point screen_point(event->x, event->y); - View::ConvertPointToScreen(GetWindow()->GetRootView(), &screen_point); - gtk_window_begin_move_drag(GetNativeWindow(), event->button, - screen_point.x(), screen_point.y(), - event->time); - return TRUE; - } - break; - } - case HTBOTTOM: - case HTBOTTOMLEFT: - case HTBOTTOMRIGHT: - case HTGROWBOX: - case HTLEFT: - case HTRIGHT: - case HTTOP: - case HTTOPLEFT: - case HTTOPRIGHT: { - gfx::Point screen_point(event->x, event->y); - View::ConvertPointToScreen(GetWindow()->GetRootView(), &screen_point); - // TODO(beng): figure out how to get a good minimum size. - gtk_widget_set_size_request(GetNativeView(), 100, 100); - gtk_window_begin_resize_drag(GetNativeWindow(), - HitTestCodeToGDKWindowEdge(hittest_code), - event->button, screen_point.x(), - screen_point.y(), event->time); - return TRUE; - } - default: - // Everything else falls into standard client event handling... - break; - } - return NativeWidgetGtk::OnButtonPress(widget, event); -} - -gboolean NativeWindowGtk::OnConfigureEvent(GtkWidget* widget, - GdkEventConfigure* event) { - SaveWindowPosition(); - return FALSE; -} - -gboolean NativeWindowGtk::OnMotionNotify(GtkWidget* widget, - GdkEventMotion* event) { - GdkEventMotion transformed_event = *event; - TransformEvent(&transformed_event); - gfx::Point translated_location(transformed_event.x, transformed_event.y); - - // Update the cursor for the screen edge. - int hittest_code = - GetWindow()->non_client_view()->NonClientHitTest(translated_location); - if (hittest_code != HTCLIENT) { - GdkCursorType cursor_type = HitTestCodeToGdkCursorType(hittest_code); - gdk_window_set_cursor(widget->window, gfx::GetCursor(cursor_type)); - } - - return NativeWidgetGtk::OnMotionNotify(widget, event); -} - -void NativeWindowGtk::OnSizeAllocate(GtkWidget* widget, - GtkAllocation* allocation) { - NativeWidgetGtk::OnSizeAllocate(widget, allocation); - - // The Window's NonClientView may provide a custom shape for the Window. - gfx::Path window_mask; - GetWindow()->non_client_view()->GetWindowMask(gfx::Size(allocation->width, - allocation->height), - &window_mask); - GdkRegion* mask_region = window_mask.CreateNativeRegion(); - gdk_window_shape_combine_region(GetNativeView()->window, mask_region, 0, 0); - if (mask_region) - gdk_region_destroy(mask_region); - - SaveWindowPosition(); -} - -gboolean NativeWindowGtk::OnLeaveNotify(GtkWidget* widget, - GdkEventCrossing* event) { - gdk_window_set_cursor(widget->window, gfx::GetCursor(GDK_LEFT_PTR)); - - return NativeWidgetGtk::OnLeaveNotify(widget, event); -} - -void NativeWindowGtk::InitNativeWidget(const Widget::InitParams& params) { - NativeWidgetGtk::InitNativeWidget(params); - - g_signal_connect(G_OBJECT(GetNativeWindow()), "configure-event", - G_CALLBACK(CallConfigureEvent), this); -} - -//////////////////////////////////////////////////////////////////////////////// // NativeWindowGtk, NativeWindow implementation: NativeWidget* NativeWindowGtk::AsNativeWidget() { @@ -205,10 +36,6 @@ const NativeWidget* NativeWindowGtk::AsNativeWidget() const { return this; } -void NativeWindowGtk::BecomeModal() { - gtk_window_set_modal(GetNativeWindow(), true); -} - Window* NativeWindowGtk::GetWindow() { return delegate_->AsWindow(); } @@ -218,36 +45,8 @@ const Window* NativeWindowGtk::GetWindow() const { } //////////////////////////////////////////////////////////////////////////////// -// NativeWindowGtk, NativeWidgetGtk overrides: - -gboolean NativeWindowGtk::OnWindowStateEvent(GtkWidget* widget, - GdkEventWindowState* event) { - if (!(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) - SaveWindowPosition(); - return NativeWidgetGtk::OnWindowStateEvent(widget, event); -} - -//////////////////////////////////////////////////////////////////////////////// // NativeWindowGtk, private: -// static -gboolean NativeWindowGtk::CallConfigureEvent(GtkWidget* widget, - GdkEventConfigure* event, - NativeWindowGtk* window_gtk) { - return window_gtk->OnConfigureEvent(widget, event); -} - -void NativeWindowGtk::SaveWindowPosition() { - // The delegate may have gone away on us. - if (!GetWindow()->window_delegate()) - return; - - bool maximized = window_state_ & GDK_WINDOW_STATE_MAXIMIZED; - GetWindow()->window_delegate()->SaveWindowPlacement( - GetWidget()->GetWindowScreenBounds(), - maximized); -} - //////////////////////////////////////////////////////////////////////////////// // NativeWindow, public: diff --git a/views/window/native_window_gtk.h b/views/window/native_window_gtk.h index e3e5799..9988a23 100644 --- a/views/window/native_window_gtk.h +++ b/views/window/native_window_gtk.h @@ -9,21 +9,12 @@ #include "base/basictypes.h" #include "views/widget/native_widget_gtk.h" #include "views/window/native_window.h" -#include "views/window/window.h" - -namespace gfx { -class Point; -class Size; -}; namespace views { namespace internal { class NativeWindowDelegate; } -class Client; -class WindowDelegate; - // Window implementation for Gtk. class NativeWindowGtk : public NativeWidgetGtk, public NativeWindow { public: @@ -33,52 +24,18 @@ class NativeWindowGtk : public NativeWidgetGtk, public NativeWindow { virtual Window* GetWindow() OVERRIDE; virtual const Window* GetWindow() const OVERRIDE; - // Overridden from NativeWidgetGtk: - virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event); - virtual gboolean OnConfigureEvent(GtkWidget* widget, - GdkEventConfigure* event); - virtual gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event); - virtual void OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation); - virtual gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event); - protected: - virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; - // Overridden from NativeWindow: virtual NativeWidget* AsNativeWidget() OVERRIDE; virtual const NativeWidget* AsNativeWidget() const OVERRIDE; - virtual void BecomeModal() OVERRIDE; - - // Overridden from NativeWidgetGtk: - virtual gboolean OnWindowStateEvent(GtkWidget* widget, - GdkEventWindowState* event) OVERRIDE; // For the constructor. friend class Window; private: - static gboolean CallConfigureEvent(GtkWidget* widget, - GdkEventConfigure* event, - NativeWindowGtk* window_gtk); - - // Asks the delegate if any to save the window's location and size. - void SaveWindowPosition(); - // A delegate implementation that handles events received here. internal::NativeWindowDelegate* delegate_; - // Our window delegate. - WindowDelegate* window_delegate_; - - // The View that provides the non-client area of the window (title bar, - // window controls, sizing borders etc). To use an implementation other than - // the default, this class must be subclassed and this value set to the - // desired implementation before calling |Init|. - NonClientView* non_client_view_; - - // Set to true if the window is in the process of closing. - bool window_closed_; - DISALLOW_COPY_AND_ASSIGN(NativeWindowGtk); }; diff --git a/views/window/native_window_win.cc b/views/window/native_window_win.cc index 611b6fe..a902d10 100644 --- a/views/window/native_window_win.cc +++ b/views/window/native_window_win.cc @@ -4,19 +4,12 @@ #include "views/window/native_window_win.h" -#include <dwmapi.h> -#include <shellapi.h> - #include "base/i18n/rtl.h" -#include "base/win/scoped_gdi_object.h" -#include "base/win/win_util.h" -#include "base/win/windows_version.h" #include "ui/base/accessibility/accessibility_types.h" #include "ui/base/keycodes/keyboard_code_conversion_win.h" #include "ui/base/l10n/l10n_util_win.h" #include "ui/base/theme_provider.h" #include "ui/base/win/hwnd_util.h" -#include "ui/gfx/canvas_skia_paint.h" #include "ui/gfx/font.h" #include "ui/gfx/path.h" #include "views/accessibility/native_view_accessibility_win.h" @@ -26,48 +19,6 @@ #include "views/window/window_delegate.h" namespace views { -namespace { - -// The thickness of an auto-hide taskbar in pixels. -static const int kAutoHideTaskbarThicknessPx = 2; - -bool GetMonitorAndRects(const RECT& rect, - HMONITOR* monitor, - gfx::Rect* monitor_rect, - gfx::Rect* work_area) { - DCHECK(monitor); - DCHECK(monitor_rect); - DCHECK(work_area); - *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); - if (!*monitor) - return false; - MONITORINFO monitor_info = { 0 }; - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(*monitor, &monitor_info); - *monitor_rect = monitor_info.rcMonitor; - *work_area = monitor_info.rcWork; - return true; -} - -// Returns true if edge |edge| (one of ABE_LEFT, TOP, RIGHT, or BOTTOM) of -// monitor |monitor| has an auto-hiding taskbar that's always-on-top. -bool EdgeHasTopmostAutoHideTaskbar(UINT edge, HMONITOR monitor) { - APPBARDATA taskbar_data = { 0 }; - taskbar_data.cbSize = sizeof APPBARDATA; - taskbar_data.uEdge = edge; - HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, - &taskbar_data)); - return ::IsWindow(taskbar) && (monitor != NULL) && - (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && - (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST); -} - -HWND GetOwner(HWND window) { - return ::GetWindow(window, GW_OWNER); -} - -} // namespace - namespace internal { void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, @@ -114,12 +65,7 @@ void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, NativeWindowWin::NativeWindowWin(internal::NativeWindowDelegate* delegate) : NativeWidgetWin(delegate->AsNativeWidgetDelegate()), - delegate_(delegate), - restored_enabled_(false), - ignore_window_pos_changes_(false), - ignore_pos_changes_factory_(this), - is_right_mouse_pressed_on_caption_(false), - last_monitor_(NULL) { + delegate_(delegate) { is_window_ = true; // Initialize these values to 0 so that subclasses can override the default // behavior before calling Init. @@ -130,367 +76,6 @@ NativeWindowWin::NativeWindowWin(internal::NativeWindowDelegate* delegate) NativeWindowWin::~NativeWindowWin() { } -// static -gfx::Font NativeWindowWin::GetWindowTitleFont() { - NONCLIENTMETRICS ncm; - base::win::GetNonClientMetrics(&ncm); - l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); - base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); - return gfx::Font(caption_font); -} - -/////////////////////////////////////////////////////////////////////////////// -// NativeWindowWin, NativeWidgetWin overrides: - -void NativeWindowWin::InitNativeWidget(const Widget::InitParams& params) { - if (window_style() == 0) - set_window_style(CalculateWindowStyle()); - if (window_ex_style() == 0) - set_window_ex_style(CalculateWindowExStyle()); - - GetMonitorAndRects(params.bounds.ToRECT(), &last_monitor_, - &last_monitor_rect_, &last_work_area_); - - NativeWidgetWin::InitNativeWidget(params); -} - -void NativeWindowWin::OnDestroy() { - RestoreEnabledIfNecessary(); - NativeWidgetWin::OnDestroy(); -} - -LRESULT NativeWindowWin::OnMouseRange(UINT message, - WPARAM w_param, - LPARAM l_param) { - if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { - is_right_mouse_pressed_on_caption_ = false; - ReleaseCapture(); - // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() - // expect screen coordinates. - CPoint screen_point(l_param); - MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_point, 1); - w_param = SendMessage(GetNativeView(), WM_NCHITTEST, 0, - MAKELPARAM(screen_point.x, screen_point.y)); - if (w_param == HTCAPTION || w_param == HTSYSMENU) { - UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD; - if (base::i18n::IsRTL()) - flags |= TPM_RIGHTALIGN; - HMENU system_menu = GetSystemMenu(GetNativeView(), FALSE); - int id = TrackPopupMenu(system_menu, flags, screen_point.x, - screen_point.y, 0, GetNativeView(), NULL); - ExecuteSystemMenuCommand(id); - return 0; - } - } else if (message == WM_NCLBUTTONDOWN && - !GetWindow()->ShouldUseNativeFrame()) { - switch (w_param) { - case HTCLOSE: - case HTMINBUTTON: - case HTMAXBUTTON: { - // When the mouse is pressed down in these specific non-client areas, - // we need to tell the RootView to send the mouse pressed event (which - // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ - // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be - // sent by the applicable button's ButtonListener. We _have_ to do this - // way rather than letting Windows just send the syscommand itself (as - // would happen if we never did this dance) because for some insane - // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed - // window control button appearance, in the Windows classic style, over - // our view! Ick! By handling this message we prevent Windows from - // doing this undesirable thing, but that means we need to roll the - // sys-command handling ourselves. - // Combine |w_param| with common key state message flags. - w_param |= ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)? MK_CONTROL : 0; - w_param |= ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)? MK_SHIFT : 0; - } - } - } else if (message == WM_NCRBUTTONDOWN && - (w_param == HTCAPTION || w_param == HTSYSMENU)) { - is_right_mouse_pressed_on_caption_ = true; - // We SetMouseCapture() to ensure we only show the menu when the button - // down and up are both on the caption. Note: this causes the button up to - // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. - SetMouseCapture(); - } - - /* - TODO(beng): This fixes some situations where the windows-classic appearance - non-client area is rendered over our custom frame, however it - causes mouse-releases to the non-client area to be eaten, so it - can't be enabled. - if (message == WM_NCLBUTTONDOWN) { - // NativeWindowWin::OnNCLButtonDown set the message as un-handled. This - // normally means NativeWidgetWin::ProcessWindowMessage will pass it to - // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird - // non-client painting, so we need to call it directly here inside a - // scoped update lock. - ScopedRedrawLock lock(this); - NativeWidgetWin::OnMouseRange(message, w_param, l_param); - DefWindowProc(GetNativeView(), WM_NCLBUTTONDOWN, w_param, l_param); - SetMsgHandled(TRUE); - } - */ - - NativeWidgetWin::OnMouseRange(message, w_param, l_param); - return 0; -} - -LRESULT NativeWindowWin::OnNCCalcSize(BOOL mode, LPARAM l_param) { - // We only override the default handling if we need to specify a custom - // non-client edge width. Note that in most cases "no insets" means no - // custom width, but in fullscreen mode we want a custom width of 0. - gfx::Insets insets = GetClientAreaInsets(); - if (insets.empty() && !IsFullscreen()) - return NativeWidgetWin::OnNCCalcSize(mode, l_param); - - RECT* client_rect = mode ? - &reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0] : - reinterpret_cast<RECT*>(l_param); - client_rect->left += insets.left(); - client_rect->top += insets.top(); - client_rect->bottom -= insets.bottom(); - client_rect->right -= insets.right(); - if (IsMaximized()) { - // Find all auto-hide taskbars along the screen edges and adjust in by the - // thickness of the auto-hide taskbar on each such edge, so the window isn't - // treated as a "fullscreen app", which would cause the taskbars to - // disappear. - HMONITOR monitor = MonitorFromWindow(GetNativeView(), - MONITOR_DEFAULTTONULL); - if (!monitor) { - // We might end up here if the window was previously minimized and the - // user clicks on the taskbar button to restore it in the previously - // maximized position. In that case WM_NCCALCSIZE is sent before the - // window coordinates are restored to their previous values, so our - // (left,top) would probably be (-32000,-32000) like all minimized - // windows. So the above MonitorFromWindow call fails, but if we check - // the window rect given with WM_NCCALCSIZE (which is our previous - // restored window position) we will get the correct monitor handle. - monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); - if (!monitor) { - // This is probably an extreme case that we won't hit, but if we don't - // intersect any monitor, let us not adjust the client rect since our - // window will not be visible anyway. - return 0; - } - } - if (EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor)) - client_rect->left += kAutoHideTaskbarThicknessPx; - if (EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor)) { - if (GetWindow()->ShouldUseNativeFrame()) { - // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of - // WM_NCHITTEST, having any nonclient area atop the window causes the - // caption buttons to draw onscreen but not respond to mouse - // hover/clicks. - // So for a taskbar at the screen top, we can't push the - // client_rect->top down; instead, we move the bottom up by one pixel, - // which is the smallest change we can make and still get a client area - // less than the screen size. This is visibly ugly, but there seems to - // be no better solution. - --client_rect->bottom; - } else { - client_rect->top += kAutoHideTaskbarThicknessPx; - } - } - if (EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor)) - client_rect->right -= kAutoHideTaskbarThicknessPx; - if (EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor)) - client_rect->bottom -= kAutoHideTaskbarThicknessPx; - - // We cannot return WVR_REDRAW when there is nonclient area, or Windows - // exhibits bugs where client pixels and child HWNDs are mispositioned by - // the width/height of the upper-left nonclient area. - return 0; - } - - // If the window bounds change, we're going to relayout and repaint anyway. - // Returning WVR_REDRAW avoids an extra paint before that of the old client - // pixels in the (now wrong) location, and thus makes actions like resizing a - // window from the left edge look slightly less broken. - // We special case when left or top insets are 0, since these conditions - // actually require another repaint to correct the layout after glass gets - // turned on and off. - if (insets.left() == 0 || insets.top() == 0) - return 0; - return mode ? WVR_REDRAW : 0; -} - -namespace { -struct ClipState { - // The window being painted. - HWND parent; - - // DC painting to. - HDC dc; - - // Origin of the window in terms of the screen. - int x; - int y; -}; - -// See comments in OnNCPaint for details of this function. -static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { - ClipState* clip_state = reinterpret_cast<ClipState*>(param); - if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { - RECT bounds; - GetWindowRect(window, &bounds); - ExcludeClipRect(clip_state->dc, - bounds.left - clip_state->x, - bounds.top - clip_state->y, - bounds.right - clip_state->x, - bounds.bottom - clip_state->y); - } - return TRUE; -} -} // namespace - -void NativeWindowWin::OnNCPaint(HRGN rgn) { - // We only do non-client painting if we're not using the native frame. - // It's required to avoid some native painting artifacts from appearing when - // the window is resized. - if (GetWindow()->ShouldUseNativeFrame()) { - NativeWidgetWin::OnNCPaint(rgn); - return; - } - - // We have an NC region and need to paint it. We expand the NC region to - // include the dirty region of the root view. This is done to minimize - // paints. - CRect window_rect; - GetWindowRect(&window_rect); - - if (window_rect.Width() != GetWidget()->GetRootView()->width() || - window_rect.Height() != GetWidget()->GetRootView()->height()) { - // If the size of the window differs from the size of the root view it - // means we're being asked to paint before we've gotten a WM_SIZE. This can - // happen when the user is interactively resizing the window. To avoid - // mass flickering we don't do anything here. Once we get the WM_SIZE we'll - // reset the region of the window which triggers another WM_NCPAINT and - // all is well. - return; - } - - CRect dirty_region; - // A value of 1 indicates paint all. - if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { - dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); - } else { - RECT rgn_bounding_box; - GetRgnBox(rgn, &rgn_bounding_box); - if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) - return; // Dirty region doesn't intersect window bounds, bale. - - // rgn_bounding_box is in screen coordinates. Map it to window coordinates. - OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); - } - - // In theory GetDCEx should do what we want, but I couldn't get it to work. - // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell - // it doesn't work at all. So, instead we get the DC for the window then - // manually clip out the children. - HDC dc = GetWindowDC(GetNativeView()); - ClipState clip_state; - clip_state.x = window_rect.left; - clip_state.y = window_rect.top; - clip_state.parent = GetNativeView(); - clip_state.dc = dc; - EnumChildWindows(GetNativeView(), &ClipDCToChild, - reinterpret_cast<LPARAM>(&clip_state)); - - gfx::Rect old_paint_region = invalid_rect(); - - if (!old_paint_region.IsEmpty()) { - // The root view has a region that needs to be painted. Include it in the - // region we're going to paint. - - CRect old_paint_region_crect = old_paint_region.ToRECT(); - CRect tmp = dirty_region; - UnionRect(&dirty_region, &tmp, &old_paint_region_crect); - } - - GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region)); - - // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap - // the following in a block to force paint to occur so that we can release - // the dc. - { - gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, - dirty_region.top, dirty_region.Width(), - dirty_region.Height()); - delegate_->AsNativeWidgetDelegate()->OnNativeWidgetPaint(&canvas); - } - - ReleaseDC(GetNativeView(), dc); - // When using a custom frame, we want to avoid calling DefWindowProc() since - // that may render artifacts. - SetMsgHandled(!GetWindow()->ShouldUseNativeFrame()); -} - -void NativeWindowWin::OnWindowPosChanging(WINDOWPOS* window_pos) { - if (ignore_window_pos_changes_) { - // If somebody's trying to toggle our visibility, change the nonclient area, - // change our Z-order, or activate us, we should probably let it go through. - if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | - SWP_FRAMECHANGED)) && - (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { - // Just sizing/moving the window; ignore. - window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; - window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); - } - } else if (!GetParent()) { - CRect window_rect; - HMONITOR monitor; - gfx::Rect monitor_rect, work_area; - if (GetWindowRect(&window_rect) && - GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { - if (monitor && (monitor == last_monitor_) && - (IsFullscreen() || ((monitor_rect == last_monitor_rect_) && - (work_area != last_work_area_)))) { - // A rect for the monitor we're on changed. Normally Windows notifies - // us about this (and thus we're reaching here due to the SetWindowPos() - // call in OnSettingChange() above), but with some software (e.g. - // nVidia's nView desktop manager) the work area can change asynchronous - // to any notification, and we're just sent a SetWindowPos() call with a - // new (frequently incorrect) position/size. In either case, the best - // response is to throw away the existing position/size information in - // |window_pos| and recalculate it based on the new work rect. - gfx::Rect new_window_rect; - if (IsFullscreen()) { - new_window_rect = monitor_rect; - } else if (IsZoomed()) { - new_window_rect = work_area; - int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); - new_window_rect.Inset(-border_thickness, -border_thickness); - } else { - new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area); - } - window_pos->x = new_window_rect.x(); - window_pos->y = new_window_rect.y(); - window_pos->cx = new_window_rect.width(); - window_pos->cy = new_window_rect.height(); - // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child - // HWNDs for some reason. - window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); - window_pos->flags |= SWP_NOCOPYBITS; - - // Now ignore all immediately-following SetWindowPos() changes. Windows - // likes to (incorrectly) recalculate what our position/size should be - // and send us further updates. - ignore_window_pos_changes_ = true; - DCHECK(ignore_pos_changes_factory_.empty()); - MessageLoop::current()->PostTask(FROM_HERE, - ignore_pos_changes_factory_.NewRunnableMethod( - &NativeWindowWin::StopIgnoringPosChanges)); - } - last_monitor_ = monitor; - last_monitor_rect_ = monitor_rect; - last_work_area_ = work_area; - } - } - - NativeWidgetWin::OnWindowPosChanging(window_pos); -} - //////////////////////////////////////////////////////////////////////////////// // NativeWindowWin, NativeWindow implementation: @@ -510,58 +95,6 @@ const NativeWidget* NativeWindowWin::AsNativeWidget() const { return this; } -void NativeWindowWin::BecomeModal() { - // We implement modality by crawling up the hierarchy of windows starting - // at the owner, disabling all of them so that they don't receive input - // messages. - HWND start = GetOwner(GetNativeView()); - while (start) { - ::EnableWindow(start, FALSE); - start = ::GetParent(start); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWindowWin, private: - -void NativeWindowWin::RestoreEnabledIfNecessary() { - if (delegate_->IsModal() && !restored_enabled_) { - restored_enabled_ = true; - // If we were run modally, we need to undo the disabled-ness we inflicted on - // the owner's parent hierarchy. - HWND start = GetOwner(GetNativeView()); - while (start) { - ::EnableWindow(start, TRUE); - start = ::GetParent(start); - } - } -} - -DWORD NativeWindowWin::CalculateWindowStyle() { - DWORD window_styles = - WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_SYSMENU | WS_CAPTION; - bool can_resize = GetWindow()->window_delegate()->CanResize(); - bool can_maximize = GetWindow()->window_delegate()->CanMaximize(); - if (can_maximize) { - window_styles |= WS_OVERLAPPEDWINDOW; - } else if (can_resize) { - window_styles |= WS_OVERLAPPED | WS_THICKFRAME; - } - if (delegate_->IsDialogBox()) { - window_styles |= DS_MODALFRAME; - // NOTE: Turning this off means we lose the close button, which is bad. - // Turning it on though means the user can maximize or size the window - // from the system menu, which is worse. We may need to provide our own - // menu to get the close button to appear properly. - // window_styles &= ~WS_SYSMENU; - } - return window_styles; -} - -DWORD NativeWindowWin::CalculateWindowExStyle() { - return delegate_->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0; -} - //////////////////////////////////////////////////////////////////////////////// // NativeWindow, public: diff --git a/views/window/native_window_win.h b/views/window/native_window_win.h index 3243739..956a3ac 100644 --- a/views/window/native_window_win.h +++ b/views/window/native_window_win.h @@ -8,13 +8,6 @@ #include "views/widget/native_widget_win.h" #include "views/window/native_window.h" -#include "views/window/window.h" - -namespace gfx { -class Font; -class Point; -class Size; -}; namespace views { namespace internal { @@ -29,9 +22,6 @@ void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, } // namespace internal -class Client; -class WindowDelegate; - //////////////////////////////////////////////////////////////////////////////// // // NativeWindowWin @@ -47,9 +37,6 @@ class NativeWindowWin : public NativeWidgetWin, explicit NativeWindowWin(internal::NativeWindowDelegate* delegate); virtual ~NativeWindowWin(); - // Returns the system set window title font. - static gfx::Font GetWindowTitleFont(); - // Overridden from NativeWindow: virtual Window* GetWindow() OVERRIDE; virtual const Window* GetWindow() const OVERRIDE; @@ -57,62 +44,14 @@ class NativeWindowWin : public NativeWidgetWin, protected: friend Window; - // Overridden from NativeWidgetWin: - virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; - virtual void OnDestroy() OVERRIDE; - virtual LRESULT OnMouseRange(UINT message, - WPARAM w_param, - LPARAM l_param) OVERRIDE; - LRESULT OnNCCalcSize(BOOL mode, LPARAM l_param); // Don't override. - virtual void OnNCPaint(HRGN rgn) OVERRIDE; - virtual void OnWindowPosChanging(WINDOWPOS* window_pos) OVERRIDE; - // Overridden from NativeWindow: virtual NativeWidget* AsNativeWidget() OVERRIDE; virtual const NativeWidget* AsNativeWidget() const OVERRIDE; - virtual void BecomeModal() OVERRIDE; private: - // If necessary, enables all ancestors. - void RestoreEnabledIfNecessary(); - - // Calculate the appropriate window styles for this window. - DWORD CalculateWindowStyle(); - DWORD CalculateWindowExStyle(); - - // Stops ignoring SetWindowPos() requests (see below). - void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; } - - // Update accessibility information via our WindowDelegate. - void UpdateAccessibleName(std::wstring& accessible_name); - void UpdateAccessibleRole(); - void UpdateAccessibleState(); - // A delegate implementation that handles events received here. internal::NativeWindowDelegate* delegate_; - // Whether all ancestors have been enabled. This is only used if is_modal_ is - // true. - bool restored_enabled_; - - // When true, this flag makes us discard incoming SetWindowPos() requests that - // only change our position/size. (We still allow changes to Z-order, - // activation, etc.) - bool ignore_window_pos_changes_; - - // The following factory is used to ignore SetWindowPos() calls for short time - // periods. - ScopedRunnableMethodFactory<NativeWindowWin> ignore_pos_changes_factory_; - - // Set to true when the user presses the right mouse button on the caption - // area. We need this so we can correctly show the context menu on mouse-up. - bool is_right_mouse_pressed_on_caption_; - - // The last-seen monitor containing us, and its rect and work area. These are - // used to catch updates to the rect and work area and react accordingly. - HMONITOR last_monitor_; - gfx::Rect last_monitor_rect_, last_work_area_; - DISALLOW_COPY_AND_ASSIGN(NativeWindowWin); }; diff --git a/views/window/window.cc b/views/window/window.cc index d16ff6c..ecfaa57 100644 --- a/views/window/window.cc +++ b/views/window/window.cc @@ -6,8 +6,6 @@ #include "base/string_util.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "ui/base/l10n/l10n_font_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/font.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" @@ -29,9 +27,7 @@ Window::InitParams::InitParams(WindowDelegate* window_delegate) } Window::Window() - : native_window_(NULL), - saved_maximized_state_(false), - minimum_size_(100, 100) { + : native_window_(NULL) { } Window::~Window() { @@ -52,25 +48,6 @@ Window* Window::CreateChromeWindow(gfx::NativeWindow parent, return window; } -// static -int Window::GetLocalizedContentsWidth(int col_resource_id) { - return ui::GetLocalizedContentsWidthForFont(col_resource_id, - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); -} - -// static -int Window::GetLocalizedContentsHeight(int row_resource_id) { - return ui::GetLocalizedContentsHeightForFont(row_resource_id, - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); -} - -// static -gfx::Size Window::GetLocalizedContentsSize(int col_resource_id, - int row_resource_id) { - return gfx::Size(GetLocalizedContentsWidth(col_resource_id), - GetLocalizedContentsHeight(row_resource_id)); -} - void Window::InitWindow(const InitParams& params) { native_window_ = params.native_window ? params.native_window @@ -82,22 +59,11 @@ void Window::InitWindow(const InitParams& params) { modified_params.widget_init_params.native_widget = native_window_->AsNativeWidget(); Init(modified_params.widget_init_params); - OnNativeWindowCreated(modified_params.widget_init_params.bounds); } //////////////////////////////////////////////////////////////////////////////// // Window, Widget overrides: -void Window::Show() { - native_window_->AsNativeWidget()->ShowNativeWidget( - saved_maximized_state_ ? NativeWidget::SHOW_MAXIMIZED - : NativeWidget::SHOW_RESTORED); - // |saved_maximized_state_| only applies the first time the window is shown. - // If we don't reset the value the window will be shown maximized every time - // it is subsequently shown after being hidden. - saved_maximized_state_ = false; -} - Window* Window::AsWindow() { return this; } @@ -109,75 +75,8 @@ const Window* Window::AsWindow() const { //////////////////////////////////////////////////////////////////////////////// // Window, internal::NativeWindowDelegate implementation: -bool Window::IsModal() const { - return widget_delegate()->IsModal(); -} - -bool Window::IsDialogBox() const { - return !!widget_delegate()->AsDialogDelegate(); -} - -void Window::OnNativeWindowCreated(const gfx::Rect& bounds) { - if (widget_delegate()->IsModal()) - native_window_->BecomeModal(); - - UpdateWindowTitle(); - SetInitialBounds(bounds); -} - internal::NativeWidgetDelegate* Window::AsNativeWidgetDelegate() { return this; } -//////////////////////////////////////////////////////////////////////////////// -// Window, private: - -void Window::SetInitialBounds(const gfx::Rect& bounds) { - // First we obtain the window's saved show-style and store it. We need to do - // this here, rather than in Show() because by the time Show() is called, - // the window's size will have been reset (below) and the saved maximized - // state will have been lost. Sadly there's no way to tell on Windows when - // a window is restored from maximized state, so we can't more accurately - // track maximized state independently of sizing information. - widget_delegate()->GetSavedMaximizedState( - &saved_maximized_state_); - - // Restore the window's placement from the controller. - gfx::Rect saved_bounds = bounds; - if (widget_delegate()->GetSavedWindowBounds(&saved_bounds)) { - if (!widget_delegate()->ShouldRestoreWindowSize()) { - saved_bounds.set_size(non_client_view()->GetPreferredSize()); - } else { - // Make sure the bounds are at least the minimum size. - if (saved_bounds.width() < minimum_size_.width()) { - saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(), - saved_bounds.right() + minimum_size_.width() - - saved_bounds.width(), - saved_bounds.bottom()); - } - - if (saved_bounds.height() < minimum_size_.height()) { - saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(), - saved_bounds.right(), - saved_bounds.bottom() + minimum_size_.height() - - saved_bounds.height()); - } - } - - // Widget's SetBounds method does not further modify the bounds that are - // passed to it. - SetBounds(saved_bounds); - } else { - if (bounds.IsEmpty()) { - // No initial bounds supplied, so size the window to its content and - // center over its parent. - native_window_->AsNativeWidget()->CenterWindow( - non_client_view()->GetPreferredSize()); - } else { - // Use the supplied initial bounds. - SetBoundsConstrained(bounds, NULL); - } - } -} - } // namespace views diff --git a/views/window/window.h b/views/window/window.h index 4fb8321..e3a36d5 100644 --- a/views/window/window.h +++ b/views/window/window.h @@ -11,7 +11,6 @@ #include "views/window/native_window_delegate.h" namespace gfx { -class Font; class Rect; class Size; } // namespace gfx @@ -50,22 +49,11 @@ class Window : public Widget, const gfx::Rect& bounds, WindowDelegate* window_delegate); - // Returns the preferred size of the contents view of this window based on - // its localized size data. The width in cols is held in a localized string - // resource identified by |col_resource_id|, the height in the same fashion. - // TODO(beng): This should eventually live somewhere else, probably closer to - // ClientView. - static int GetLocalizedContentsWidth(int col_resource_id); - static int GetLocalizedContentsHeight(int row_resource_id); - static gfx::Size GetLocalizedContentsSize(int col_resource_id, - int row_resource_id); - // Initializes the window. Must be called before any post-configuration // operations are performed. void InitWindow(const InitParams& params); // Overridden from Widget: - virtual void Show() OVERRIDE; virtual Window* AsWindow() OVERRIDE; virtual const Window* AsWindow() const OVERRIDE; @@ -81,24 +69,11 @@ class Window : public Widget, protected: // Overridden from NativeWindowDelegate: - virtual bool IsModal() const OVERRIDE; - virtual bool IsDialogBox() const OVERRIDE; - virtual void OnNativeWindowCreated(const gfx::Rect& bounds) OVERRIDE; virtual internal::NativeWidgetDelegate* AsNativeWidgetDelegate() OVERRIDE; private: - // Sizes and positions the window just after it is created. - void SetInitialBounds(const gfx::Rect& bounds); - NativeWindow* native_window_; - // The saved maximized state for this window. See note in SetInitialBounds - // that explains why we save this. - bool saved_maximized_state_; - - // The smallest size the window can be. - gfx::Size minimum_size_; - DISALLOW_COPY_AND_ASSIGN(Window); }; |