Want to trigger function on mouse click in browser window via C++ plugin, written with FireBreath.
But with code below nothing happends on click.
Got the following code in TestPluginAPI.h:
BEGIN_PLUGIN_EVENT_MAP()
EVENTTYPE_CASE(FB::MouseDownEvent, onMouseDown, FB::PluginWindow)
END_PLUGIN_EVENT_MAP()
virtual bool onMouseDown(FB::MouseDownEvent *evt, FB::PluginWindow *);
And this code in testPluginAPI.cpp:
bool TestPluginAPI::onMouseDown(FB::MouseDownEvent *evt, FB::PluginWindow *)
{
if(evt->m_Btn == FB::MouseButtonEvent::MouseButton_Left)
{
fire_showcrd(FB::variant_list_of(evt->m_x)(evt->m_y));
}
return 0;
}
Fire_showcrd(...) was tested separately and it's working. It seems that something wrong with click detecter part, but what?
The PluginWindow events in FireBreath only apply to the region where the plugin lives, not elsewhere on the webpage. You'll only get events when the plugin itself is clicked on using this method, and if any DOM elements are hovering over your plugin (even if your plugin draws in front because it's windowed) you may end up losing the events to that element.
You can get click events for the whole page by using javascript.
Related
I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.
I'm working on an application for taking screenshots on Windows, OSX and Linux in C++/Qt. Now I need to set global hotkeys, so the user can take screenshots when the application is running in the background. I tried with Qxt and UGlobalHotkey, which are both Qt libraries, but neither of them seemed to work.
I tried to implement it for OSX with Carbon (tutorial), but I need to call a class member function, which just doesn't work. Could someone provide me with an example? You can find my code here. The function i need to call is new_screenshot().
Or is there any other way to achieve something like this? I really need my application to take a screenshot from the background, otherwise it's pretty useless (yes, I should probably have implemented it at the very beginning to see if it even works). Would it maybe be better to have a separate client for every platform (Cocoa Swift for OSX, GTK for Linux, C# client for Windows)? I have often thought about this the past few days.
Do I understand correctly that you want to call new_screenshot from the hot key event handler? If so, InstallApplicationEventHandler lets you pass a pointer to user data in 4th argument. Pass a pointer to your MainWindow instance (based on code from the tutorial):
MainWindow *mainWindow = ... // get main window somehow
InstallApplicationEventHandler(&MyHotKeyHandler,1,&eventType,mainWindow,NULL);
Then you can use it in the event handler.
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
{
//Do something once the key is pressed
static_cast<MainWindow*>(userData)->new_screenshot();
return noErr;
}
I did something in the past with MFC and WIN32 API....so it only works on Windows...but pressing ALT+F10 was able to hide/show a window...
void CWinHideDlg::OnButtonActive()
{
CString tmp;
GetDlgItemText(IDC_BUTTON_ACTIVE,tmp);
if(0 == strcmp(tmp.GetBuffer(tmp.GetLength()),"Activate"))
{
m_myAtom=GlobalAddAtom("MY_GLOBAL_HOT_HIDE_KEY");
int err=RegisterHotKey(this->GetSafeHwnd(),m_myAtom,MOD_ALT,VK_F10);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Stop");
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(TRUE);
SetDlgItemText(IDC_STATIC_INFO,"Set the mouse over the window \nand press ALT + F10 to hide it...");
}
else
{
UnregisterHotKey(this->GetSafeHwnd(),m_myAtom);
GlobalDeleteAtom(m_myAtom);
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(FALSE);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Activate");
}
}
Basically this code activates/deactivates the hot key ALT+F10, once it activates you can hide/unhide a running window on the system by setting the mouse pointer over the window and press ALT+F10...
This is from the WindowProc function:
if(message == WM_HOTKEY)
{
CString tmp;
POINT pc;
GetCursorPos(&pc);
if(GetAsyncKeyState(VK_F10))
{
HWND hwnd=::WindowFromPoint(pc);
if(hwnd)
{
tmp.Format("%08Xh",hwnd);
m_HideWins.InsertString(m_HideWins.GetCount(),tmp);
::ShowWindow(hwnd,SW_HIDE);
}
}
}
You can use the code to register your own HOT Key and use it to take a screenshot...
Hope it helps...
I've written at QT5/QML application that I wish to be able to control remotely from a web browser. I'm sending a screen shot out to the browser and receiving position as x,y in return. I've written a C++ class to do this.
When I start the application, the image comes up and the first click from the browser works. The second does not work. If I click anywhere in the application, I see the button press happen on the screen, and then the remote press will work one more time.
I suspect the problem is that the focus changes and it's not being reset properly until I click on the actual application.
I'm using QGuiApplicatiop and so far haven't been able to get anythong useful out of itemAt or widgetAt so I can send the event to the appropriate object.
Here's a code snippet to show what I'm doing.
void Wremote::mClick(int x, int y ) {
QPointF *pos = new QPointF((qreal)x,(qreal)y);
QWindowList wl = QGuiApplication::allWindows();
_mwindow = wl[0];
QMouseEvent pevent(QEvent::MouseButtonPress,
*pos, Qt::LeftButton
,Qt::LeftButton, 0);
QMouseEvent revent(QEvent::MouseButtonRelease,
*pos, Qt::LeftButton
,Qt::LeftButton, 0);
QApplication::instance() ->sendEvent(_mwindow, &pevent);
QApplication::processEvents();
QApplication::instance() ->sendEvent(_mwindow, &revent);
QApplication::processEvents();
}
This is my first foray into integrating Qt with C++. The bulk of the application uses pyotherside.
Any pointers to where I'm going wrong would be appreciated.
Thanks,
Steve
Here's a working solution if not an elegant one.
void Wremote::mClick(int x, int y ) {
QTest::mouseClick(_mwindow,Qt::LeftButton,0,QPoint(x,y),-1);
}
I've run into a bit of an issue related to a whitelist Web Browser my company has been developing / maintaining for one of our product lines. The browser runs on top of Qt 4.8.6, using qtwebkit (Migration to 5.X would be ideal, but the embedded Linux OS we're using is too old to support the newer versions based on our testing, and upgrading to a newer OS is too costly to us / our customers). The primary interface to the browser is a 6x8 touchscreen, mounted inside an aircraft cockpit.
For sites that have things like scrollable/embedded maps (ex. Google Maps), the users of the browser want the ability to drag the entire page when they are selecting something outside of the map, and drag just the map (without the entire page scrolling) when the map is selected (Ala most of the popular mobile browsers).
Thus far, I am able to do one or the other, but not both:
When I hook mouse handlers into a QWebView or QGraphicsWebView, I can turn the cursor into a hand and very easily support dragging of the entire web page. However, that inhibits the page's ability to handle the mouse events for when a user is pulling over a map (i.e. When a user drags over a map, it drags the entire page without moving the map).
When I don't add in the hooks to handle mouse events, things like maps are scrollable by grapping/dragging them, but of course the user loses the ability to drag the entire page.
Right now, the browser uses the later, with scroll bars disabled and a directional-arrow overlay to allow the user to scroll the entire page (as the display size is limited, and scrollbars take up too much space when they are sized large enough for the user to interact with them)...but this is not ideal.
My Question: Is there any easy way to make it so that the page, and elements in a page, can be scrolled seamlessly?
Thanks!
Rob
Seems to me like you need to check if you are over such a map and ignore(pass along) the event in that case. I think you should be able to do something like this:
bool GraphicsWebView::isOverMap(QPoint pos) {
QWebPage* webPage = this->page();
if (webPage) {
QWebFrame* webFrame = webPage->frameAt(pos);
if (webFrame) {
QString selectorQuery = "#map-canvas"; // Based on https://developers.google.com/maps/tutorials/fundamentals/adding-a-google-map
QList<QWebElement> list = webFrame->findAllElements(selectorQuery).toList(); // Find all the maps!
foreach(QWebElement element, list) {
if (element.geometry().contains(pos)) {
return true; // Cursor is over a map
}
}
}
}
return false; // No match
}
Obviously this is a pretty specific function but there is probably a way to come up with a better selector query that will apply to all those kinds of QWebElement.
Assuming you hook mouse events by subclassing QGraphicsWebView and reimplementing void mouseMoveEvent(QGraphicsSceneMouseEvent * event), I suggest you do something like:
void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
if (isOverMap(mapFromScene(event->scenePos()).toPoint())) { // We got a map!
event.ignore(); // Clear the accept flag
return; // Return, we're done here
}
handleMoveView(); // Not over any maps, let's scroll the page
}
This part of the doc explains how events are handled with regard to the topmost item. I especially recommend you read the third paragraph.
Hope that helps!
EDIT: Did a bit more research and it looks like something like that could be more generic:
graphicsView.focusItem()->flags().testFlag(QGraphicsItem::ItemIsMovable);
It's at the very least worth investigating as a replacement to isOverMap()
EDIT: Gotcha, here is something you can try then.
Start by subclassing QGraphicsSceneMouseEvent and add a signal called void destroyedWithoutAccept() that's emitted in the destructor if the event has not been accepted.
Then modify mouseMoveEvent to look like this:
void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
MyEvent myEvent = new MyEvent(event); // Copy event
event.accept(); // accept original event
connect(myEvent, SIGNAL(destroyedWithoutAccept),
this, SLOT(handleMoveView)); // Callback if unused
QGraphicsWebView::mouseMoveEvent(myEvent); // Pass it to Base class
}
If that works, it might introduce a bit of delay if deleteLater is used to destroy it. But in that case reimplement it as well.
Update: Answer: Two normal lines of code required. Thanks Noseratio!
I banged my head on the keyboard for more hours than I would have cared to trying to simulate IEs Ctrl+N behavior in my hosted Browser control app. Unfortunately, due to complications which I've abstracted out of my code examples below, I can't just let IE do Ctlr+N itself. So I have to do it manually.
Keep in mind that I am running a hosted browser. So typically, opening links in new windows will actuall open it within a new "tab" within my application (it's not really a tab, but another window... but appearance-wise it's a tab). However, Ctrl+N is different -- here, it is expected a fully-fledged IE window will launch when pressed.
I think my problem is that of framing the questions -- admittedly I am new to WebBrowser control and I find it to be a lot of yucky. Regardless, I've scoured the Internet for the past day and couldn't come up with an elegant solution.
Basically, the ideal solution would be to call a "NewWindow" function within WebBrowser control or its affiliate libraries; however, all I was able to find where the *On*NewWindow methods, which were event handlers, not event signallers. Which I understand that most of the time, the user will be creating the events... but what about programmatic simulation?
I tried looking into an SENDMESSAGE approach where I could use the IDs that the OnNewWindow events use... that ended up in nothing than crashes. Perhaps I could go back to get it work, but I'd like confirmation is that approach is even worth my time.
The next approach, which should have been the most elegeant, but sadly didn't pan out, was like the following:
Navigate2(GetLocationURL().GetBuffer(), BrowserNavConstants::navOpenInNewWindow);
It would have worked marvelously if it weren't for the fact that the new window would open in the background, blinking in the taskbar. needing clicking to bring it to the front.
I tried to get around the limitation in a myriad of ways, including getting the dispatcher of the current context, then calling OnNewWindow2 with that IDispatch object. Then I would invoke QueryInterface on the dispatch object for an IWebBrowser control. The webBrowser control (presumably under the control of the new window) could then navigate to the page of the original context. However... this too was a pretty messy solution and in the end would cause crashes.
Finally, I resorted to manually invoking JavaScript to get the desired behavior. Really?? Was there really no more elegant a solution to my problem than the below mess of code?
if ((pMsg->wParam == 'N') && (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
{
LPDISPATCH pDisp = CHtmlView::GetHtmlDocument();
IHTMLDocument2 *pDoc;
if (SUCCEEDED(pDisp->QueryInterface(IID_IHTMLDocument2, (void **)&pDoc)))
{
IHTMLWindow2* pWnd;
pDoc->get_parentWindow(&pWnd);
BSTR bStrLang = ::SysAllocString(L"JavaScript");
CString sCode(L"window.open(\"");
sCode.Append(GetLocationURL().GetBuffer());
sCode.Append(L"\");");
BSTR bStrCode = sCode.AllocSysString();
COleVariant retVal;
pWnd->execScript(bStrCode, bStrLang, retVal);
::SysFreeString(bStrLang);
::SysFreeString(bStrCode);
pDoc->Release();
}
pDisp->Release();
I find it hard to believe that I must resort to such hackery as this to get something as simple as opening a new window when the user presses Ctrl+N.
Please stackoverflow, please point out the clearly obvious thing I overlooked.
Ctrl-N in IE starts a new window on the same session. In your case, window.open or webBrowser.Navigate2 will create a window on a new session, because it will be run by iexplore.exe process which is separate from your app. The session is shared per-process, this is how the underlying UrlMon library works. So you'll loose all cookies and authentication cache for the new window. On the other hand, when you create a new window which hosts WebBrowser control within your own app process, you'll keep the session.
If such behavior is OK for your needs, try first your initial Navigate2 approach, precededing it with AllowSetForegroundWindow(ASFW_ANY) call. If the new window still doesn't receive the focus correctly, you can try creating an instance of InternetExplorer.Application out-of-proc COM object, and use the same IWebBrowser2 interface to automate it. Below is a simple C# app which works OK for me, the new window is correctly brought to the foreground, no focus issues. It should not be a problem to do the same with MFC.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace IeApp
{
public partial class MainForm : Form
{
// get the underlying WebBrowser ActiveX object;
// this code depends on SHDocVw.dll COM interop assembly,
// generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
// and add as a reference to the project
public MainForm()
{
InitializeComponent();
}
private void NewWindow_Click(object sender, EventArgs e)
{
AllowSetForegroundWindow(ASFW_ANY);
// could do: var ie = new SHDocVw.InternetExplorer()
var ie = (SHDocVw.InternetExplorer)Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
ie.Visible = true;
ie.Navigate("http://www.example.com");
}
const int ASFW_ANY = -1;
[DllImport("user32.dll")]
static extern bool AllowSetForegroundWindow(int dwProcessId);
}
}