in libcef/browser/native/native_menu_win.cc [226:386]
void OnDrawItem(UINT w_param, DRAWITEMSTRUCT* draw_item_struct) {
HDC dc = draw_item_struct->hDC;
COLORREF prev_bg_color, prev_text_color;
// Set background color and text color
if (draw_item_struct->itemState & ODS_SELECTED) {
prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
prev_text_color = SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_MENU));
if (draw_item_struct->itemState & ODS_DISABLED)
prev_text_color = SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
else
prev_text_color = SetTextColor(dc, GetSysColor(COLOR_MENUTEXT));
}
if (draw_item_struct->itemData) {
CefNativeMenuWin::ItemData* data =
GetItemData(draw_item_struct->itemData);
// Draw the background.
HBRUSH hbr = CreateSolidBrush(GetBkColor(dc));
FillRect(dc, &draw_item_struct->rcItem, hbr);
DeleteObject(hbr);
// Draw the label.
RECT rect = draw_item_struct->rcItem;
rect.top += kItemTopMargin;
// Should we add kIconWidth only when icon.width() != 0 ?
rect.left += kItemLeftMargin + kIconWidth;
rect.right -= kItemLabelSpacing;
UINT format = DT_TOP | DT_SINGLELINE;
// Check whether the mnemonics should be underlined.
BOOL underline_mnemonics;
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0);
if (!underline_mnemonics)
format |= DT_HIDEPREFIX;
gfx::FontList font_list;
HFONT new_font = CreateNativeFont(font_list.GetPrimaryFont());
HGDIOBJ old_font = SelectObject(dc, new_font);
// If an accelerator is specified (with a tab delimiting the rest of the
// label from the accelerator), we have to justify the fist part on the
// left and the accelerator on the right.
// TODO(jungshik): This will break in RTL UI. Currently, he/ar use the
// window system UI font and will not hit here.
std::wstring label = data->label;
std::wstring accel;
std::wstring::size_type tab_pos = label.find(L'\t');
if (tab_pos != std::wstring::npos) {
accel = label.substr(tab_pos);
label = label.substr(0, tab_pos);
}
DrawTextEx(dc, const_cast<wchar_t*>(label.data()),
static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL);
if (!accel.empty()) {
DrawTextEx(dc, const_cast<wchar_t*>(accel.data()),
static_cast<int>(accel.size()), &rect, format | DT_RIGHT,
NULL);
}
SelectObject(dc, old_font);
DeleteObject(new_font);
ui::MenuModel::ItemType type =
data->native_menu_win->model_->GetTypeAt(data->model_index);
// Draw the icon after the label, otherwise it would be covered
// by the label.
ui::ImageModel icon =
data->native_menu_win->model_->GetIconAt(data->model_index);
if (icon.IsImage() || icon.IsVectorIcon()) {
ui::NativeTheme* native_theme =
ui::NativeTheme::GetInstanceForNativeUi();
// Logic from Widget::GetColorProviderKey() prior to
// https://crrev.com/e24ffe177b.
// TODO(cef): Use |native_theme->GetColorProviderKey(nullptr)| after M97
// Chromium update.
const auto color_scheme = native_theme->GetDefaultSystemColorScheme();
ui::ColorProviderManager::Key color_provider_key(
(color_scheme == ui::NativeTheme::ColorScheme::kDark)
? ui::ColorProviderManager::ColorMode::kDark
: ui::ColorProviderManager::ColorMode::kLight,
(color_scheme ==
ui::NativeTheme::ColorScheme::kPlatformHighContrast)
? ui::ColorProviderManager::ContrastMode::kHigh
: ui::ColorProviderManager::ContrastMode::kNormal,
native_theme->is_custom_system_theme()
? ui::ColorProviderManager::SystemTheme::kCustom
: ui::ColorProviderManager::SystemTheme::kDefault,
/*custom_theme=*/nullptr);
auto* color_provider =
ui::ColorProviderManager::Get().GetColorProviderFor(
color_provider_key);
// We currently don't support items with both icons and checkboxes.
const gfx::ImageSkia skia_icon =
icon.IsImage() ? icon.GetImage().AsImageSkia()
: ui::ThemedVectorIcon(icon.GetVectorIcon())
.GetImageSkia(color_provider, 16);
DCHECK(type != ui::MenuModel::TYPE_CHECK);
std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
skia_icon.width(), skia_icon.height(), false);
canvas->drawImage(skia_icon.bitmap()->asImage(), 0, 0);
DrawToNativeContext(
canvas.get(), dc, draw_item_struct->rcItem.left + kItemLeftMargin,
draw_item_struct->rcItem.top +
(draw_item_struct->rcItem.bottom -
draw_item_struct->rcItem.top - skia_icon.height()) /
2,
NULL);
} else if (type == ui::MenuModel::TYPE_CHECK &&
data->native_menu_win->model_->IsItemCheckedAt(
data->model_index)) {
// Manually render a checkbox.
const MenuConfig& config = MenuConfig::instance();
NativeTheme::State state;
if (draw_item_struct->itemState & ODS_DISABLED) {
state = NativeTheme::kDisabled;
} else {
state = draw_item_struct->itemState & ODS_SELECTED
? NativeTheme::kHovered
: NativeTheme::kNormal;
}
std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
config.check_width, config.check_height, false);
cc::SkiaPaintCanvas paint_canvas(canvas.get());
NativeTheme::ExtraParams extra;
extra.menu_check.is_radio = false;
gfx::Rect bounds(0, 0, config.check_width, config.check_height);
// Draw the background and the check.
ui::NativeTheme* native_theme =
ui::NativeTheme::GetInstanceForNativeUi();
native_theme->Paint(&paint_canvas, NativeTheme::kMenuCheckBackground,
state, bounds, extra);
native_theme->Paint(&paint_canvas, NativeTheme::kMenuCheck, state,
bounds, extra);
// Draw checkbox to menu.
DrawToNativeContext(
canvas.get(), dc, draw_item_struct->rcItem.left + kItemLeftMargin,
draw_item_struct->rcItem.top +
(draw_item_struct->rcItem.bottom -
draw_item_struct->rcItem.top - config.check_height) /
2,
NULL);
}
} else {
// Draw the separator
draw_item_struct->rcItem.top +=
(draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top) / 3;
DrawEdge(dc, &draw_item_struct->rcItem, EDGE_ETCHED, BF_TOP);
}
SetBkColor(dc, prev_bg_color);
SetTextColor(dc, prev_text_color);
}