From 914dce6cc2b9eab4c95a377935dd947901682e4e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 1 Nov 2014 13:44:28 +0800 Subject: [PATCH] win: Add color chooser dialog --- atom.gyp | 8 ++ .../browser/ui/views/color_chooser_dialog.cc | 85 ++++++++++++++ .../browser/ui/views/color_chooser_dialog.h | 73 ++++++++++++ .../browser/ui/views/color_chooser_win.cc | 104 ++++++++++++++++++ 4 files changed, 270 insertions(+) create mode 100644 chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc create mode 100644 chromium_src/chrome/browser/ui/views/color_chooser_dialog.h create mode 100644 chromium_src/chrome/browser/ui/views/color_chooser_win.cc diff --git a/atom.gyp b/atom.gyp index 842f199b832..101304d1cdf 100644 --- a/atom.gyp +++ b/atom.gyp @@ -332,6 +332,11 @@ 'chromium_src/library_loaders/libspeechd.h', '<@(native_mate_files)', ], + 'lib_sources_win': [ + 'chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc', + 'chromium_src/chrome/browser/ui/views/color_chooser_dialog.h', + 'chromium_src/chrome/browser/ui/views/color_chooser_win.cc', + ], 'framework_sources': [ 'atom/app/atom_library_main.cc', 'atom/app/atom_library_main.h', @@ -551,6 +556,9 @@ ], 'conditions': [ ['OS=="win"', { + 'sources': [ + '<@(lib_sources_win)', + ], 'link_settings': { 'libraries': [ '-limm32.lib', diff --git a/chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc b/chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc new file mode 100644 index 00000000000..347f1043aac --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/color_chooser_dialog.h" + +#include + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread.h" +#include "content/public/browser/browser_thread.h" +#include "skia/ext/skia_utils_win.h" +#include "ui/views/color_chooser/color_chooser_listener.h" +#include "ui/views/win/hwnd_util.h" + +using content::BrowserThread; + +// static +COLORREF ColorChooserDialog::g_custom_colors[16]; + +ColorChooserDialog::ExecuteOpenParams::ExecuteOpenParams(SkColor color, + RunState run_state, + HWND owner) + : color(color), + run_state(run_state), + owner(owner) { +} + +ColorChooserDialog::ColorChooserDialog(views::ColorChooserListener* listener, + SkColor initial_color, + gfx::NativeWindow owning_window) + : listener_(listener) { + DCHECK(listener_); + CopyCustomColors(g_custom_colors, custom_colors_); + HWND owning_hwnd = views::HWNDForNativeWindow(owning_window); + ExecuteOpenParams execute_params(initial_color, BeginRun(owning_hwnd), + owning_hwnd); + execute_params.run_state.dialog_thread->message_loop()->PostTask(FROM_HERE, + base::Bind(&ColorChooserDialog::ExecuteOpen, this, execute_params)); +} + +ColorChooserDialog::~ColorChooserDialog() { +} + +bool ColorChooserDialog::IsRunning(gfx::NativeWindow owning_window) const { + return listener_ && IsRunningDialogForOwner( + views::HWNDForNativeWindow(owning_window)); +} + +void ColorChooserDialog::ListenerDestroyed() { + // Our associated listener has gone away, so we shouldn't call back to it if + // our worker thread returns after the listener is dead. + listener_ = NULL; +} + +void ColorChooserDialog::ExecuteOpen(const ExecuteOpenParams& params) { + CHOOSECOLOR cc; + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = params.owner; + cc.rgbResult = skia::SkColorToCOLORREF(params.color); + cc.lpCustColors = custom_colors_; + cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; + bool success = !!ChooseColor(&cc); + DisableOwner(cc.hwndOwner); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ColorChooserDialog::DidCloseDialog, this, success, + skia::COLORREFToSkColor(cc.rgbResult), params.run_state)); +} + +void ColorChooserDialog::DidCloseDialog(bool chose_color, + SkColor color, + RunState run_state) { + EndRun(run_state); + CopyCustomColors(custom_colors_, g_custom_colors); + if (listener_) { + if (chose_color) + listener_->OnColorChosen(color); + listener_->OnColorChooserDialogClosed(); + } +} + +void ColorChooserDialog::CopyCustomColors(COLORREF* src, COLORREF* dst) { + memcpy(dst, src, sizeof(COLORREF) * arraysize(g_custom_colors)); +} diff --git a/chromium_src/chrome/browser/ui/views/color_chooser_dialog.h b/chromium_src/chrome/browser/ui/views/color_chooser_dialog.h new file mode 100644 index 00000000000..8f2e6ffc8fa --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/color_chooser_dialog.h @@ -0,0 +1,73 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ +#define CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ + +#include "base/memory/ref_counted.h" +#include "chrome/browser/ui/views/color_chooser_dialog.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/shell_dialogs/base_shell_dialog.h" +#include "ui/shell_dialogs/base_shell_dialog_win.h" + +namespace views { +class ColorChooserListener; +} + +class ColorChooserDialog + : public base::RefCountedThreadSafe, + public ui::BaseShellDialog, + public ui::BaseShellDialogImpl { + public: + ColorChooserDialog(views::ColorChooserListener* listener, + SkColor initial_color, + gfx::NativeWindow owning_window); + virtual ~ColorChooserDialog(); + + // BaseShellDialog: + virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE; + virtual void ListenerDestroyed() OVERRIDE; + + private: + struct ExecuteOpenParams { + ExecuteOpenParams(SkColor color, RunState run_state, HWND owner); + SkColor color; + RunState run_state; + HWND owner; + }; + + // Called on the dialog thread to show the actual color chooser. This is + // shown modal to |params.owner|. Once it's closed, calls back to + // DidCloseDialog() on the UI thread. + void ExecuteOpen(const ExecuteOpenParams& params); + + // Called on the UI thread when a color chooser is closed. |chose_color| is + // true if the user actually chose a color, in which case |color| is the + // chosen color. Calls back to the |listener_| (if applicable) to notify it + // of the results, and copies the modified array of |custom_colors_| back to + // |g_custom_colors| so future dialogs will see the changes. + void DidCloseDialog(bool chose_color, SkColor color, RunState run_state); + + // Copies the array of colors in |src| to |dst|. + void CopyCustomColors(COLORREF*, COLORREF*); + + // The user's custom colors. Kept process-wide so that they can be persisted + // from one dialog invocation to the next. + static COLORREF g_custom_colors[16]; + + // A copy of the custom colors for the current dialog to display and modify. + // This allows us to safely access the colors even if multiple windows are + // simultaneously showing color choosers (which would cause thread safety + // problems if we gave them direct handles to |g_custom_colors|). + COLORREF custom_colors_[16]; + + // The listener to notify when the user closes the dialog. This may be set to + // NULL before the color chooser is closed, signalling that the listener no + // longer cares about the outcome. + views::ColorChooserListener* listener_; + + DISALLOW_COPY_AND_ASSIGN(ColorChooserDialog); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ diff --git a/chromium_src/chrome/browser/ui/views/color_chooser_win.cc b/chromium_src/chrome/browser/ui/views/color_chooser_win.cc new file mode 100644 index 00000000000..1a2aed45c38 --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/color_chooser_win.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/browser/ui/views/color_chooser_aura.h" +#include "chrome/browser/ui/views/color_chooser_dialog.h" +#include "content/public/browser/color_chooser.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "ui/views/color_chooser/color_chooser_listener.h" + +class ColorChooserWin : public content::ColorChooser, + public views::ColorChooserListener { + public: + static ColorChooserWin* Open(content::WebContents* web_contents, + SkColor initial_color); + + ColorChooserWin(content::WebContents* web_contents, + SkColor initial_color); + ~ColorChooserWin(); + + // content::ColorChooser overrides: + virtual void End() OVERRIDE; + virtual void SetSelectedColor(SkColor color) OVERRIDE {} + + // views::ColorChooserListener overrides: + virtual void OnColorChosen(SkColor color); + virtual void OnColorChooserDialogClosed(); + + private: + static ColorChooserWin* current_color_chooser_; + + // The web contents invoking the color chooser. No ownership. because it will + // outlive this class. + content::WebContents* web_contents_; + + // The color chooser dialog which maintains the native color chooser UI. + scoped_refptr color_chooser_dialog_; +}; + +ColorChooserWin* ColorChooserWin::current_color_chooser_ = NULL; + +ColorChooserWin* ColorChooserWin::Open(content::WebContents* web_contents, + SkColor initial_color) { + if (current_color_chooser_) + return NULL; + current_color_chooser_ = new ColorChooserWin(web_contents, initial_color); + return current_color_chooser_; +} + +ColorChooserWin::ColorChooserWin(content::WebContents* web_contents, + SkColor initial_color) + : web_contents_(web_contents) { + gfx::NativeWindow owning_window = (gfx::NativeWindow)::GetAncestor( + (HWND)web_contents->GetRenderViewHost()->GetView()->GetNativeView(), + GA_ROOT); + color_chooser_dialog_ = new ColorChooserDialog(this, + initial_color, + owning_window); +} + +ColorChooserWin::~ColorChooserWin() { + // Always call End() before destroying. + DCHECK(!color_chooser_dialog_); +} + +void ColorChooserWin::End() { + // The ColorChooserDialog's listener is going away. Ideally we'd + // programmatically close the dialog at this point. Since that's impossible, + // we instead tell the dialog its listener is going away, so that the dialog + // doesn't try to communicate with a destroyed listener later. (We also tell + // the renderer the dialog is closed, since from the renderer's perspective + // it effectively is.) + OnColorChooserDialogClosed(); +} + +void ColorChooserWin::OnColorChosen(SkColor color) { + if (web_contents_) + web_contents_->DidChooseColorInColorChooser(color); +} + +void ColorChooserWin::OnColorChooserDialogClosed() { + if (color_chooser_dialog_.get()) { + color_chooser_dialog_->ListenerDestroyed(); + color_chooser_dialog_ = NULL; + } + DCHECK(current_color_chooser_ == this); + current_color_chooser_ = NULL; + if (web_contents_) + web_contents_->DidEndColorChooser(); +} + +namespace chrome { + +content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, + SkColor initial_color) { + return ColorChooserWin::Open(web_contents, initial_color); +} + +} // namespace chrome