зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1663784 - Implement basic nsISystemStatusBar service for Windows. r=mhowell
This basic implementation uses the application icon for the icon that goes into the system tray for now. A later patch in this series will change that icon to one defined within the associated <xul:menu> element's styles. Differential Revision: https://phabricator.services.mozilla.com/D89787
This commit is contained in:
Родитель
58ca6739aa
Коммит
9f45e1f865
|
@ -63,7 +63,6 @@ elif toolkit == 'cocoa':
|
|||
'nsIMacSharingService.idl',
|
||||
'nsIMacWebAppUtils.idl',
|
||||
'nsIStandaloneNativeMenu.idl',
|
||||
'nsISystemStatusBar.idl',
|
||||
'nsITaskbarProgress.idl',
|
||||
'nsITouchBarHelper.idl',
|
||||
'nsITouchBarInput.idl',
|
||||
|
@ -120,6 +119,7 @@ XPIDL_SOURCES += [
|
|||
'nsIScreenManager.idl',
|
||||
'nsISharePicker.idl',
|
||||
'nsISound.idl',
|
||||
'nsISystemStatusBar.idl',
|
||||
'nsITransferable.idl',
|
||||
'nsIUserIdleService.idl',
|
||||
'nsIUserIdleServiceInternal.idl',
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sts=2 sw=2 et cin: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <strsafe.h>
|
||||
#include "SystemStatusBar.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsMenuFrame.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
||||
using mozilla::LinkedListElement;
|
||||
using mozilla::dom::Element;
|
||||
|
||||
class StatusBarEntry final : public LinkedListElement<RefPtr<StatusBarEntry>> {
|
||||
public:
|
||||
explicit StatusBarEntry(Element* aMenu);
|
||||
nsresult Init();
|
||||
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||
const Element* GetMenu() { return mMenu; };
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(StatusBarEntry)
|
||||
|
||||
private:
|
||||
~StatusBarEntry();
|
||||
RefPtr<Element> mMenu;
|
||||
NOTIFYICONDATAW mIconData;
|
||||
boolean mInitted;
|
||||
};
|
||||
|
||||
StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) {
|
||||
mIconData = {/* cbSize */ sizeof(NOTIFYICONDATA),
|
||||
/* hWnd */ 0,
|
||||
/* uID */ 2,
|
||||
/* uFlags */ NIF_ICON | NIF_MESSAGE | NIF_TIP,
|
||||
/* uCallbackMessage */ WM_USER,
|
||||
/* hIcon */ 0,
|
||||
/* szTip */ L"", // This is updated in Init()
|
||||
/* dwState */ 0,
|
||||
/* dwStateMask */ 0,
|
||||
/* szInfo */ L"",
|
||||
/* uVersion */ {NOTIFYICON_VERSION},
|
||||
/* szInfoTitle */ L"",
|
||||
/* dwInfoFlags */ 0};
|
||||
}
|
||||
|
||||
StatusBarEntry::~StatusBarEntry() {
|
||||
if (!mInitted) {
|
||||
return;
|
||||
}
|
||||
::Shell_NotifyIconW(NIM_DELETE, &mIconData);
|
||||
VERIFY(::DestroyWindow(mIconData.hWnd));
|
||||
}
|
||||
|
||||
nsresult StatusBarEntry::Init() {
|
||||
HWND iconWindow;
|
||||
NS_ENSURE_TRUE(iconWindow = ::CreateWindowExW(
|
||||
/* extended style */ 0,
|
||||
/* className */ L"IconWindowClass",
|
||||
/* title */ 0,
|
||||
/* style */ WS_CAPTION,
|
||||
/* x, y, cx, cy */ 0, 0, 0, 0,
|
||||
/* parent */ 0,
|
||||
/* menu */ 0,
|
||||
/* instance */ 0,
|
||||
/* create struct */ 0),
|
||||
NS_ERROR_FAILURE);
|
||||
::SetWindowLongPtr(iconWindow, GWLP_USERDATA, (LONG_PTR)this);
|
||||
|
||||
mIconData.hWnd = iconWindow;
|
||||
mIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), IDI_APPLICATION);
|
||||
|
||||
nsAutoString labelAttr;
|
||||
mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::label, labelAttr);
|
||||
const nsString& label = PromiseFlatString(labelAttr);
|
||||
|
||||
size_t destLength = sizeof mIconData.szTip / (sizeof mIconData.szTip[0]);
|
||||
wchar_t* tooltip = &(mIconData.szTip[0]);
|
||||
::StringCchCopyNW(tooltip, destLength, label.get(), label.Length());
|
||||
|
||||
::Shell_NotifyIconW(NIM_ADD, &mIconData);
|
||||
::Shell_NotifyIconW(NIM_SETVERSION, &mIconData);
|
||||
|
||||
mInitted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LRESULT StatusBarEntry::OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
if (msg == WM_USER && (lp == WM_LBUTTONUP || lp == WM_RBUTTONUP)) {
|
||||
nsMenuFrame* menu = do_QueryFrame(mMenu->GetPrimaryFrame());
|
||||
if (!menu) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
nsMenuPopupFrame* popupFrame = menu->GetPopup();
|
||||
if (!popupFrame) {
|
||||
return TRUE;
|
||||
}
|
||||
nsIWidget* widget = popupFrame->GetNearestWidget();
|
||||
if (!widget) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HWND win = static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
|
||||
if (!win) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The menu that is being opened is a Gecko <xul:menu>, and the popup code
|
||||
// that manages it expects that the window that the <xul:menu> belongs to
|
||||
// will be in the foreground when it opens. If we don't do this, then if the
|
||||
// icon is clicked when the window is _not_ in the foreground, then the
|
||||
// opened menu will not be keyboard focusable, nor will it close on its own
|
||||
// if the user clicks away from the menu (at least, not until the user
|
||||
// focuses any window in the parent process).
|
||||
::SetForegroundWindow(win);
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
POINT pt;
|
||||
::GetCursorPos(&pt);
|
||||
pm->ShowPopup(popupFrame->GetContent(), nullptr, EmptyString(), pt.x, pt.y,
|
||||
false, false, true, nullptr);
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(SystemStatusBar, nsISystemStatusBar)
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
StatusBarEntry* entry =
|
||||
(StatusBarEntry*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
if (entry) {
|
||||
return entry->OnMessage(hWnd, msg, wp, lp);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static StaticRefPtr<SystemStatusBar> sSingleton;
|
||||
|
||||
SystemStatusBar& SystemStatusBar::GetSingleton() {
|
||||
if (!sSingleton) {
|
||||
sSingleton = new SystemStatusBar();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
return *sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<SystemStatusBar> SystemStatusBar::GetAddRefedSingleton() {
|
||||
RefPtr<SystemStatusBar> sm = &GetSingleton();
|
||||
return sm.forget();
|
||||
}
|
||||
|
||||
nsresult SystemStatusBar::Init() {
|
||||
WNDCLASS classStruct = {/* style */ 0,
|
||||
/* lpfnWndProc */ &WindowProc,
|
||||
/* cbClsExtra */ 0,
|
||||
/* cbWndExtra */ 0,
|
||||
/* hInstance */ 0,
|
||||
/* hIcon */ 0,
|
||||
/* hCursor */ 0,
|
||||
/* hbrBackground */ 0,
|
||||
/* lpszMenuName */ 0,
|
||||
/* lpszClassName */ L"IconWindowClass"};
|
||||
NS_ENSURE_TRUE(::RegisterClass(&classStruct), NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SystemStatusBar::AddItem(Element* aElement) {
|
||||
RefPtr<StatusBarEntry> entry = new StatusBarEntry(aElement);
|
||||
nsresult rv = entry->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mStatusBarEntries.insertBack(entry);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SystemStatusBar::RemoveItem(Element* aElement) {
|
||||
for (StatusBarEntry* entry : mStatusBarEntries) {
|
||||
if (entry->GetMenu() == aElement) {
|
||||
entry->removeFrom(mStatusBarEntries);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
} // namespace mozilla::widget
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef widget_windows_SystemStatusBar_h
|
||||
#define widget_windows_SystemStatusBar_h
|
||||
|
||||
#include "nsISystemStatusBar.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
class StatusBarEntry;
|
||||
|
||||
class SystemStatusBar final : public nsISystemStatusBar {
|
||||
public:
|
||||
explicit SystemStatusBar() = default;
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISYSTEMSTATUSBAR
|
||||
|
||||
static SystemStatusBar& GetSingleton();
|
||||
static already_AddRefed<SystemStatusBar> GetAddRefedSingleton();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
~SystemStatusBar() = default;
|
||||
mozilla::LinkedList<RefPtr<StatusBarEntry>> mStatusBarEntries;
|
||||
};
|
||||
|
||||
} // namespace mozilla::widget
|
||||
|
||||
#endif // widget_windows_SystemStatusBar_h
|
|
@ -156,6 +156,16 @@ Classes = [
|
|||
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
|
||||
'overridable': True,
|
||||
},
|
||||
{
|
||||
'cid': '{b6e1a890-b2b8-4883-a65f-9476f6185313}',
|
||||
'contract_ids': ['@mozilla.org/widget/systemstatusbar;1'],
|
||||
'singleton': True,
|
||||
'init_method': 'Init',
|
||||
'type': 'mozilla::widget::SystemStatusBar',
|
||||
'constructor': 'mozilla::widget::SystemStatusBar::GetAddRefedSingleton',
|
||||
'headers': ['/widget/windows/SystemStatusBar.h'],
|
||||
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
|
||||
},
|
||||
]
|
||||
|
||||
if buildconfig.substs['CC_TYPE'] in ('msvc', 'clang-cl'):
|
||||
|
|
|
@ -77,6 +77,7 @@ UNIFIED_SOURCES += [
|
|||
'ProcInfo.cpp',
|
||||
'RemoteBackbuffer.cpp',
|
||||
'ScreenHelperWin.cpp',
|
||||
'SystemStatusBar.cpp',
|
||||
'TaskbarPreview.cpp',
|
||||
'TaskbarPreviewButton.cpp',
|
||||
'TaskbarTabPreview.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче