Merge pull request #972 from atom/template-image

Add support for template image
This commit is contained in:
Cheng Zhao 2015-01-02 19:43:17 -08:00
commit 31bdfc2281
16 changed files with 178 additions and 89 deletions

View file

@ -238,6 +238,7 @@
'atom/common/native_mate_converters/gurl_converter.h',
'atom/common/native_mate_converters/image_converter.cc',
'atom/common/native_mate_converters/image_converter.h',
'atom/common/native_mate_converters/image_converter_mac.mm',
'atom/common/native_mate_converters/string16_converter.h',
'atom/common/native_mate_converters/v8_value_converter.cc',
'atom/common/native_mate_converters/v8_value_converter.h',

View file

@ -12,6 +12,7 @@
#include "atom/common/native_mate_converters/string16_converter.h"
#include "native_mate/constructor.h"
#include "native_mate/dictionary.h"
#include "ui/gfx/image/image.h"
#include "atom/common/node_includes.h"
@ -19,7 +20,7 @@ namespace atom {
namespace api {
Tray::Tray(const gfx::ImageSkia& image)
Tray::Tray(const gfx::Image& image)
: tray_icon_(TrayIcon::Create()) {
tray_icon_->SetImage(image);
tray_icon_->AddObserver(this);
@ -29,7 +30,7 @@ Tray::~Tray() {
}
// static
mate::Wrappable* Tray::New(const gfx::ImageSkia& image) {
mate::Wrappable* Tray::New(const gfx::Image& image) {
return new Tray(image);
}
@ -57,13 +58,13 @@ void Tray::Destroy() {
tray_icon_.reset();
}
void Tray::SetImage(mate::Arguments* args, const gfx::ImageSkia& image) {
void Tray::SetImage(mate::Arguments* args, const gfx::Image& image) {
if (!CheckTrayLife(args))
return;
tray_icon_->SetImage(image);
}
void Tray::SetPressedImage(mate::Arguments* args, const gfx::ImageSkia& image) {
void Tray::SetPressedImage(mate::Arguments* args, const gfx::Image& image) {
if (!CheckTrayLife(args))
return;
tray_icon_->SetPressedImage(image);
@ -92,7 +93,7 @@ void Tray::DisplayBalloon(mate::Arguments* args,
if (!CheckTrayLife(args))
return;
gfx::ImageSkia icon;
gfx::Image icon;
options.Get("icon", &icon);
base::string16 title, content;
if (!options.Get("title", &title) ||

View file

@ -12,7 +12,7 @@
#include "base/memory/scoped_ptr.h"
namespace gfx {
class ImageSkia;
class Image;
}
namespace mate {
@ -31,13 +31,13 @@ class Menu;
class Tray : public mate::EventEmitter,
public TrayIconObserver {
public:
static mate::Wrappable* New(const gfx::ImageSkia& image);
static mate::Wrappable* New(const gfx::Image& image);
static void BuildPrototype(v8::Isolate* isolate,
v8::Handle<v8::ObjectTemplate> prototype);
protected:
explicit Tray(const gfx::ImageSkia& image);
explicit Tray(const gfx::Image& image);
virtual ~Tray();
// TrayIconObserver:
@ -48,8 +48,8 @@ class Tray : public mate::EventEmitter,
void OnBalloonClosed() override;
void Destroy();
void SetImage(mate::Arguments* args, const gfx::ImageSkia& image);
void SetPressedImage(mate::Arguments* args, const gfx::ImageSkia& image);
void SetImage(mate::Arguments* args, const gfx::Image& image);
void SetPressedImage(mate::Arguments* args, const gfx::Image& image);
void SetToolTip(mate::Arguments* args, const std::string& tool_tip);
void SetTitle(mate::Arguments* args, const std::string& title);
void SetHighlightMode(mate::Arguments* args, bool highlight);

View file

@ -12,7 +12,7 @@ TrayIcon::TrayIcon() {
TrayIcon::~TrayIcon() {
}
void TrayIcon::SetPressedImage(const gfx::ImageSkia& image) {
void TrayIcon::SetPressedImage(const gfx::Image& image) {
}
void TrayIcon::SetTitle(const std::string& title) {
@ -21,7 +21,7 @@ void TrayIcon::SetTitle(const std::string& title) {
void TrayIcon::SetHighlightMode(bool highlight) {
}
void TrayIcon::DisplayBalloon(const gfx::ImageSkia& icon,
void TrayIcon::DisplayBalloon(const gfx::Image& icon,
const base::string16& title,
const base::string16& contents) {
}

View file

@ -20,10 +20,10 @@ class TrayIcon {
virtual ~TrayIcon();
// Sets the image associated with this status icon.
virtual void SetImage(const gfx::ImageSkia& image) = 0;
virtual void SetImage(const gfx::Image& image) = 0;
// Sets the image associated with this status icon when pressed.
virtual void SetPressedImage(const gfx::ImageSkia& image);
virtual void SetPressedImage(const gfx::Image& image);
// Sets the hover text for this status icon. This is also used as the label
// for the menu item which is created as a replacement for the status icon
@ -41,7 +41,7 @@ class TrayIcon {
// Displays a notification balloon with the specified contents.
// Depending on the platform it might not appear by the icon tray.
virtual void DisplayBalloon(const gfx::ImageSkia& icon,
virtual void DisplayBalloon(const gfx::Image& icon,
const base::string16& title,
const base::string16& contents);

View file

@ -22,8 +22,8 @@ class TrayIconCocoa : public TrayIcon {
TrayIconCocoa();
virtual ~TrayIconCocoa();
virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE;
virtual void SetImage(const gfx::Image& image) OVERRIDE;
virtual void SetPressedImage(const gfx::Image& image) OVERRIDE;
virtual void SetToolTip(const std::string& tool_tip) OVERRIDE;
virtual void SetTitle(const std::string& title) OVERRIDE;
virtual void SetHighlightMode(bool highlight) OVERRIDE;

View file

@ -53,20 +53,14 @@ TrayIconCocoa::~TrayIconCocoa() {
[[NSStatusBar systemStatusBar] removeStatusItem:item_];
}
void TrayIconCocoa::SetImage(const gfx::ImageSkia& image) {
if (!image.isNull()) {
gfx::Image neutral(image);
if (!neutral.IsEmpty())
[item_ setImage:neutral.ToNSImage()];
}
void TrayIconCocoa::SetImage(const gfx::Image& image) {
if (!image.IsEmpty())
[item_ setImage:image.ToNSImage()];
}
void TrayIconCocoa::SetPressedImage(const gfx::ImageSkia& image) {
if (!image.isNull()) {
gfx::Image neutral(image);
if (!neutral.IsEmpty())
[item_ setAlternateImage:neutral.ToNSImage()];
}
void TrayIconCocoa::SetPressedImage(const gfx::Image& image) {
if (!image.IsEmpty())
[item_ setAlternateImage:image.ToNSImage()];
}
void TrayIconCocoa::SetToolTip(const std::string& tool_tip) {

View file

@ -8,6 +8,7 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h"
#include "chrome/browser/ui/libgtk2ui/gtk2_status_icon.h"
#include "ui/gfx/image/image.h"
namespace atom {
@ -17,18 +18,18 @@ TrayIconGtk::TrayIconGtk() {
TrayIconGtk::~TrayIconGtk() {
}
void TrayIconGtk::SetImage(const gfx::ImageSkia& image) {
void TrayIconGtk::SetImage(const gfx::Image& image) {
if (icon_) {
icon_->SetImage(image);
icon_->SetImage(image.AsImageSkia());
return;
}
base::string16 empty;
if (libgtk2ui::AppIndicatorIcon::CouldOpen())
icon_.reset(
new libgtk2ui::AppIndicatorIcon(base::GenerateGUID(), image, empty));
icon_.reset(new libgtk2ui::AppIndicatorIcon(
base::GenerateGUID(), image.AsImageSkia(), empty));
else
icon_.reset(new libgtk2ui::Gtk2StatusIcon(image, empty));
icon_.reset(new libgtk2ui::Gtk2StatusIcon(image.AsImageSkia(), empty));
icon_->set_delegate(this);
}

View file

@ -23,7 +23,7 @@ class TrayIconGtk : public TrayIcon,
virtual ~TrayIconGtk();
// TrayIcon:
virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
virtual void SetImage(const gfx::Image& image) OVERRIDE;
virtual void SetToolTip(const std::string& tool_tip) OVERRIDE;
virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) OVERRIDE;

View file

@ -10,6 +10,7 @@
#include "base/win/windows_version.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/views/controls/menu/menu_runner.h"
@ -90,19 +91,19 @@ void NotifyIcon::ResetIcon() {
LOG(WARNING) << "Unable to re-create status tray icon.";
}
void NotifyIcon::SetImage(const gfx::ImageSkia& image) {
void NotifyIcon::SetImage(const gfx::Image& image) {
// Create the icon.
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
icon_data.uFlags = NIF_ICON;
icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap()));
icon_.Set(IconUtil::CreateHICONFromSkBitmap(image.AsBitmap()));
icon_data.hIcon = icon_.Get();
BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
if (!result)
LOG(WARNING) << "Error setting status tray icon image";
}
void NotifyIcon::SetPressedImage(const gfx::ImageSkia& image) {
void NotifyIcon::SetPressedImage(const gfx::Image& image) {
// Ignore pressed images, since the standard on Windows is to not highlight
// pressed status icons.
}
@ -118,7 +119,7 @@ void NotifyIcon::SetToolTip(const std::string& tool_tip) {
LOG(WARNING) << "Unable to set tooltip for status tray icon";
}
void NotifyIcon::DisplayBalloon(const gfx::ImageSkia& icon,
void NotifyIcon::DisplayBalloon(const gfx::Image& icon,
const base::string16& title,
const base::string16& contents) {
NOTIFYICONDATA icon_data;
@ -130,8 +131,8 @@ void NotifyIcon::DisplayBalloon(const gfx::ImageSkia& icon,
icon_data.uTimeout = 0;
base::win::Version win_version = base::win::GetVersion();
if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) {
balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
if (!icon.IsEmpty() && win_version != base::win::VERSION_PRE_XP) {
balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(icon.AsBitmap()));
icon_data.hBalloonIcon = balloon_icon_.Get();
icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
}

View file

@ -43,10 +43,10 @@ class NotifyIcon : public TrayIcon {
UINT message_id() const { return message_id_; }
// Overridden from TrayIcon:
void SetImage(const gfx::ImageSkia& image) override;
void SetPressedImage(const gfx::ImageSkia& image) override;
void SetImage(const gfx::Image& image) override;
void SetPressedImage(const gfx::Image& image) override;
void SetToolTip(const std::string& tool_tip) override;
void DisplayBalloon(const gfx::ImageSkia& icon,
void DisplayBalloon(const gfx::Image& icon,
const base::string16& title,
const base::string16& contents) override;
void SetContextMenu(ui::SimpleMenuModel* menu_model) override;

View file

@ -12,9 +12,12 @@
#include "base/strings/string_util.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/base/layout.h"
#if !defined(OS_MACOSX)
namespace mate {
namespace {
@ -29,6 +32,8 @@ ScaleFactorPair kScaleFactorPairs[] = {
{ "@2x" , 2.0f },
{ "@3x" , 3.0f },
{ "@1x" , 1.0f },
{ "@4x" , 4.0f },
{ "@5x" , 5.0f },
{ "@1.25x" , 1.25f },
{ "@1.33x" , 1.33f },
{ "@1.4x" , 1.4f },
@ -40,10 +45,6 @@ ScaleFactorPair kScaleFactorPairs[] = {
float GetScaleFactorFromPath(const base::FilePath& path) {
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
// There is no scale info in the file path.
if (!EndsWith(filename, "x", true))
return 1.0f;
// We don't try to convert string to float here because it is very very
// expensive.
for (unsigned i = 0; i < arraysize(kScaleFactorPairs); ++i) {
@ -54,27 +55,9 @@ float GetScaleFactorFromPath(const base::FilePath& path) {
return 1.0f;
}
void AppendIfExists(std::vector<base::FilePath>* paths,
const base::FilePath& path) {
if (base::PathExists(path))
paths->push_back(path);
}
void PopulatePossibleFilePaths(std::vector<base::FilePath>* paths,
const base::FilePath& path) {
AppendIfExists(paths, path);
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
if (MatchPattern(filename, "*@*x"))
return;
for (unsigned i = 0; i < arraysize(kScaleFactorPairs); ++i)
AppendIfExists(paths,
path.InsertBeforeExtensionASCII(kScaleFactorPairs[i].name));
}
bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
const base::FilePath& path) {
bool AddImageSkiaRep(gfx::ImageSkia* image,
const base::FilePath& path,
double scale_factor) {
std::string file_contents;
if (!base::ReadFileToString(path, &file_contents))
return false;
@ -89,13 +72,28 @@ bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
// Try JPEG.
decoded.reset(gfx::JPEGCodec::Decode(data, size));
if (decoded) {
image->AddRepresentation(gfx::ImageSkiaRep(
*decoded.release(), GetScaleFactorFromPath(path)));
return true;
}
if (!decoded)
return false;
return false;
image->AddRepresentation(gfx::ImageSkiaRep(*decoded.release(), scale_factor));
return true;
}
bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
const base::FilePath& path) {
bool succeed = false;
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
if (MatchPattern(filename, "*@*x"))
// Don't search for other representations if the DPI has been specified.
return AddImageSkiaRep(image, path, GetScaleFactorFromPath(path));
else
succeed |= AddImageSkiaRep(image, path, 1.0f);
for (const ScaleFactorPair& pair : kScaleFactorPairs)
succeed |= AddImageSkiaRep(image,
path.InsertBeforeExtensionASCII(pair.name),
pair.scale);
return succeed;
}
} // namespace
@ -104,20 +102,23 @@ bool Converter<gfx::ImageSkia>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::ImageSkia* out) {
base::FilePath path;
if (Converter<base::FilePath>::FromV8(isolate, val, &path)) {
std::vector<base::FilePath> paths;
PopulatePossibleFilePaths(&paths, path);
if (paths.empty())
return false;
if (!Converter<base::FilePath>::FromV8(isolate, val, &path))
return false;
for (size_t i = 0; i < paths.size(); ++i) {
if (!AddImageSkiaRepFromPath(out, paths[i]))
return false;
}
return true;
}
return PopulateImageSkiaRepsFromPath(out, path);
}
return false;
bool Converter<gfx::Image>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Image* out) {
gfx::ImageSkia image;
if (!ConvertFromV8(isolate, val, &image))
return false;
*out = gfx::Image(image);
return true;
}
} // namespace mate
#endif // !defined(OS_MACOSX)

View file

@ -8,6 +8,7 @@
#include "native_mate/converter.h"
namespace gfx {
class Image;
class ImageSkia;
}
@ -20,6 +21,13 @@ struct Converter<gfx::ImageSkia> {
gfx::ImageSkia* out);
};
template<>
struct Converter<gfx::Image> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Image* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_IMAGE_CONVERTER_H_

View file

@ -0,0 +1,61 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/image_converter.h"
#import <Cocoa/Cocoa.h>
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
namespace {
bool IsTemplateImage(const std::string& path) {
return (MatchPattern(path, "*Template.*") ||
MatchPattern(path, "*Template@*x.*"));
}
} // namespace
namespace mate {
bool Converter<gfx::ImageSkia>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::ImageSkia* out) {
gfx::Image image;
if (!ConvertFromV8(isolate, val, &image) || image.IsEmpty())
return false;
gfx::ImageSkia image_skia = image.AsImageSkia();
if (image_skia.isNull())
return false;
*out = image_skia;
return true;
}
bool Converter<gfx::Image>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Image* out) {
std::string path;
if (!ConvertFromV8(isolate, val, &path))
return false;
base::scoped_nsobject<NSImage> image([[NSImage alloc]
initByReferencingFile:base::SysUTF8ToNSString(path)]);
if (![image isValid])
return false;
if (IsTemplateImage(path))
[image setTemplate:YES];
*out = gfx::Image(image.release());
return true;
}
} // namespace mate

View file

@ -13,7 +13,10 @@ var window = new BrowserWindow({icon: '/Users/somebody/images/window.png'});
## Supported formats
Only `PNG` and `JPG` formats are supported, and `PNG` format is preferred.
On Mac all formats supported by the system can be used, while on Linux and
Windows only `PNG` and `JPG` formats are supported.
So it is recommended to use `PNG` images for all cases.
## High resolution image
@ -51,4 +54,22 @@ Following suffixes as DPI denses are also supported:
* `@2x`
* `@2.5x`
* `@3x`
* `@4x`
* `@5x`
## Template image
Template images consist of black and clear colors (and an alpha channel).
Template images are not intended to be used as standalone images and are usually
mixed with other content to create the desired final appearance.
The most common case is to use template image for menu bar icon so it can adapt
to both light and dark menu bars.
Template image is only supported on Mac.
To mark an image as template image, its filename should end with the word
`Template`, examples are:
* `xxxTemplate.png`
* `xxxTemplate@2x.png`

2
vendor/native_mate vendored

@ -1 +1 @@
Subproject commit 4a1d11b2bea0907c66bf986ab635e161a1a16385
Subproject commit be2934d9b5925cb017238c1b385340c2f9cfdcb7