Opened 6 years ago

Closed 5 years ago

Last modified 5 years ago

#15699 closed enhancement (fixed)

Allow creating wxImage from wxCursor

Reported by: neilc Owned by: VZ
Priority: low Milestone:
Component: GUI-all Version: dev-latest
Keywords: wxImage wxCursor assignment Cc:
Blocked By: Blocking:
Patch: yes

Description

It would be useful to be able to convert a wxCursor to a wxImage. This would be useful to try and 'augment' the standard cursor with a small icon. e.g. when in a particular editing mode. Or could be used to show a choice of cursors, with their images. I've managed to make some initial progress, as detailed here:

http://forums.wxwidgets.org/viewtopic.php?f=1&t=38291

for MSW/GTK. Is wxCursor::ConvertToImage() an appropriate place to create such a function? (if I'm going to try and make a patch) I've found a way that works but I'm not sure that it's the 'correct' wxWidgets way of creating wxBitmaps from HICON/GdkPixbuf.

Ideally, the wxImage would also include wxCursor hotspot info (via image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, hotSpotX) ) but I've been unable to find a way to get this info from MSW/GTK - any ideas?

---
Neil

Attachments (5)

Allow-creating-wxBitmap-from-wxCursor.patch download (974 bytes) - added by awi 5 years ago.
Patch allowing assignment wxCursor to wxBitmap.
wxBitmap-from-wxCursor.png download (15.9 KB) - added by awi 5 years ago.
Screenshot with results.
Allow-creating-wxBitmap-from-wxCursor-wxGTK-v2.patch download (1.1 KB) - added by awi 5 years ago.
Allow converting wxCursor to wxBitmap under wxGTK.
Allow-creating-wxBitmap-from-wxCursor-wxMSW-v2.patch download (904 bytes) - added by awi 5 years ago.
Allow converting wxCursor to wxBitmap under wxMSW.
Document-new-wxBitmap-ctor-v2.patch download (682 bytes) - added by awi 5 years ago.
Document new wxBitmap ctor.

Download all attachments as: .zip

Change History (17)

comment:1 Changed 6 years ago by vadz

  • Priority changed from normal to low
  • Status changed from new to confirmed
  • Summary changed from Create wxImage from wxCursor to Allow creating wxImage from wxCursor
  • Version changed from 2.9.4 to dev-latest

I think the simplest would be to convert wxCursor to wxBitmap. We already have wxBitmap::operator=(wxCursor) in wxMSW but not in the other ports AFAIK. So I think the best would be to implement it elsewhere too.

comment:2 Changed 5 years ago by awi

  • Keywords assignment added; convert removed
  • Patch set

I have attached the patch which allows assigning wxCursor to wxBitmap under wxGTK (attachment:Allow-creating-wxBitmap-from-wxCursor.patch).
Copy constructor is used deliberately in order:

  1. To avoid problems with ambiguity of overloaded assignment operators in constructs like this one:
    wxBitmap bmp;
    wxImage img;
    bmp = img;
    

where under wxGTK operator=(wxCursor&) would clash with operator=(wxBitmap&).

  1. And to handle in one place initializing wxBitmap from wxCursor:
    wxCursor cursor;
    wxBitmap bmp(cursor);
    

and assigning wxCursor to wxBitmap:

wxCursor cursor;
wxBitmap bmp;
bmp = cursor;

I think that in this case it's intuitive to use copy constructor because created object is of different type (and purpose) then the source one. Assignment operator would be rather misleading here.

Test case:

  • samples/minimal/minimal.cpp

    a b public: 
    6767    // event handlers (these functions should _not_ be virtual)
    6868    void OnQuit(wxCommandEvent& event);
    6969    void OnAbout(wxCommandEvent& event);
     70    void OnPaint(wxPaintEvent &event);
    7071
    7172private:
    7273    // any class wishing to process wxWidgets events must use this macro
    enum 
    99100wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    100101    EVT_MENU(Minimal_Quit,  MyFrame::OnQuit)
    101102    EVT_MENU(Minimal_About, MyFrame::OnAbout)
     103    EVT_PAINT(MyFrame::OnPaint)
    102104wxEND_EVENT_TABLE()
    103105
    104106// Create a new application object: this macro will allow wxWidgets to create
    bool MyApp::OnInit() 
    141143// main frame
    142144// ----------------------------------------------------------------------------
    143145
     146#include "wx/artprov.h"
     147#include "wx/bitmap.h"
     148
    144149// frame constructor
    145150MyFrame::MyFrame(const wxString& title)
    146151       : wxFrame(NULL, wxID_ANY, title)
    MyFrame::MyFrame(const wxString& title) 
    172177    CreateStatusBar(2);
    173178    SetStatusText("Welcome to wxWidgets!");
    174179#endif // wxUSE_STATUSBAR
     180    wxBitmap bmp;
     181    wxCursor cursor1(wxCURSOR_MAGNIFIER);
     182    bmp = cursor1;
     183    new wxStaticBitmap(this, wxID_ANY, bmp, wxPoint(10, 10));
     184
     185    wxCursor cursor2(wxCURSOR_ARROW);
     186    bmp = cursor2;
     187    new wxStaticBitmap(this, wxID_ANY, bmp, wxPoint(60, 10));
     188
     189    wxCursor cursor3(wxCURSOR_BULLSEYE);
     190    wxBitmap bmp2(cursor3);
     191    new wxStaticBitmap(this, wxID_ANY, bmp2, wxPoint(110, 10));
    175192}
    176193
     194void MyFrame::OnPaint(wxPaintEvent &WXUNUSED(event))
     195{
     196    wxPaintDC dc(this);
     197
     198    wxCursor cursor1(wxCURSOR_MAGNIFIER);
     199    wxBitmap bmp(cursor1);
     200    dc.DrawBitmap(bmp, wxPoint(10, 60), true);
     201
     202    wxCursor cursor2(wxCURSOR_ARROW);
     203    bmp = cursor2;
     204    dc.DrawBitmap(bmp, wxPoint(60, 60), true);
     205
     206    wxCursor cursor3(wxCURSOR_BULLSEYE);
     207    bmp = cursor3;
     208    dc.DrawBitmap(bmp, wxPoint(110, 60), true);
     209}
    177210
    178211// event handlers

Changed 5 years ago by awi

Patch allowing assignment wxCursor to wxBitmap.

Changed 5 years ago by awi

Screenshot with results.

comment:3 Changed 5 years ago by awi

And it looks like:
Screenshot with results.

comment:4 follow-up: Changed 5 years ago by neilc

This looks good and brings the wxGTK functionality in line with wxMSW.

In my testing, it seemed like using a wxBitmap in wxMSW still didn't look quite right due to transparency of the cursor not being copied. So that such info was copied, I think a wxImage would be required. Similarly, for the ultimate goal of augmenting a standard cursor, the info on the wxCursor hotspot is required somewhere, but I could not work out how to get this.

comment:5 in reply to: ↑ 4 Changed 5 years ago by awi

Replying to neilc:

In my testing, it seemed like using a wxBitmap in wxMSW still didn't look quite right due to transparency of the cursor not being copied.

Some MSW cursors can have no internal bitmap at all so in order to get transparency effect for wxBitmap created from wxCursor you need to draw this bitmap (using DrawBitmap) with 'useMask' parameter explicitly set to true (it's set to false by default).

comment:6 Changed 5 years ago by vadz

Thanks! I have a couple of reservations about this patch though:

  1. If we decide that we need a wxBitmap ctor from wxCursor, we should provide it in both wxMSW and wxGTK, it doesn't make much sense to provide only the overloaded assignment operator in the former and only the overloaded ctor in the latter. Also, this ctor probably should be explicit as this is a relatively rare operation and you don't want to copy cursors to bitmaps accidentally. Finally, whatever we decide to add to the official API, it should be documented.
  2. According to GTK+ docs, gdk_cursor_get_image() is "only" available since 2.8 and I think we're still supposed to support GTK+ 2.6, so we need the usual version checks around it.

comment:7 Changed 5 years ago by awi

That's right, one has to have very special interest in converting wxCursor to wxBitmap and hence there is no need to implement bells and whistles and one explicit ctor should be enough.
Patches implementing this conversion for wxGTK and wxMSW are attached.
There is also attached an update to the documentation.

New test case:

  • samples/minimal/minimal.cpp

    a b public: 
    6767    // event handlers (these functions should _not_ be virtual)
    6868    void OnQuit(wxCommandEvent& event);
    6969    void OnAbout(wxCommandEvent& event);
     70    void OnPaint(wxPaintEvent &event);
    7071
    7172private:
    7273    // any class wishing to process wxWidgets events must use this macro
    enum 
    99100wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    100101    EVT_MENU(Minimal_Quit,  MyFrame::OnQuit)
    101102    EVT_MENU(Minimal_About, MyFrame::OnAbout)
     103    EVT_PAINT(MyFrame::OnPaint)
    102104wxEND_EVENT_TABLE()
    103105
    104106// Create a new application object: this macro will allow wxWidgets to create
    bool MyApp::OnInit() 
    141143// main frame
    142144// ----------------------------------------------------------------------------
    143145
     146#include "wx/artprov.h"
     147#include "wx/bitmap.h"
     148
    144149// frame constructor
    145150MyFrame::MyFrame(const wxString& title)
    146151       : wxFrame(NULL, wxID_ANY, title)
    MyFrame::MyFrame(const wxString& title) 
    172177    CreateStatusBar(2);
    173178    SetStatusText("Welcome to wxWidgets!");
    174179#endif // wxUSE_STATUSBAR
     180    wxCursor cursor1(wxCURSOR_MAGNIFIER);
     181    wxBitmap bmp1(cursor1);
     182    new wxStaticBitmap(this, wxID_ANY, bmp1, wxPoint(10, 10));
     183
     184    wxCursor cursor2(wxCURSOR_ARROW);
     185    wxBitmap bmp2(cursor2);
     186    new wxStaticBitmap(this, wxID_ANY, bmp2, wxPoint(60, 10));
     187
     188    wxCursor cursor3(wxCURSOR_BULLSEYE);
     189    wxBitmap bmp3(cursor3);
     190    new wxStaticBitmap(this, wxID_ANY, bmp3, wxPoint(110, 10));
    175191}
    176192
     193void MyFrame::OnPaint(wxPaintEvent &WXUNUSED(event))
     194{
     195    wxPaintDC dc(this);
     196
     197    wxCursor cursor1(wxCURSOR_MAGNIFIER);
     198    wxBitmap bmp1(cursor1);
     199    dc.DrawBitmap(bmp1, wxPoint(10, 60), true);
     200
     201    wxCursor cursor2(wxCURSOR_ARROW);
     202    wxBitmap bmp2(cursor2);
     203    dc.DrawBitmap(bmp2, wxPoint(60, 60), true);
     204
     205    wxCursor cursor3(wxCURSOR_BULLSEYE);
     206    wxBitmap bmp3(cursor3);
     207    dc.DrawBitmap(bmp3, wxPoint(110, 60), true);
     208}
    177209
    178210// event handlers

Changed 5 years ago by awi

Allow converting wxCursor to wxBitmap under wxGTK.

Changed 5 years ago by awi

Allow converting wxCursor to wxBitmap under wxMSW.

Changed 5 years ago by awi

Document new wxBitmap ctor.

comment:8 Changed 5 years ago by VZ

  • Owner set to VZ
  • Resolution set to fixed
  • Status changed from confirmed to closed

In 77537:

Add wxBitmap ctor from wxCursor to wxGTK and wxMSW.

Allow converting wxCursor to wxBitmap in order to draw it, for example.

Closes #15699.

comment:9 Changed 5 years ago by VZ

In 77538:

Deprecate wxBitmap assignment operator from wxCursor in wxMSW.

This assignment operator is not portable and a better alternative is available
now with the addition of the wxBitmap ctor from wxCursor in the previous
commit.

See #15699.

comment:10 Changed 5 years ago by VZ

In 77541:

wxGTK PCH-less compilation build fix after r77537.

Add the required #include.

See #15699.

comment:11 Changed 5 years ago by neilc

I think this fixes half the issue, but the ultimate goal of augmenting the system cursor would require the cursor hotspot info. Shall I open a new issue or are we just going to treat that as impossible? As far as I can tell from the Windows API, this info can only be set when a cursor is created, and never retrieved.
http://msdn.microsoft.com/en-us/library/windows/desktop/ff468815%28v=vs.85%29.aspx

comment:12 Changed 5 years ago by awi

I think it is possible to retrieve hotspot coordinates from cursor (at least under MSW, but probably also under GTK+). But this not a purpose of wxBitmap to handle such kind of information. It should be done in wxCursor (and in wxImage) if there is a need to do so.
As far as I can see hotspot coordinates are not stored in the current implementation of wxCursor. I think you should submit a separate ticket with request for enhancement regarding this issue.

Note: See TracTickets for help on using tickets.