Aus zwei mach eins: Wie man zwei Cursor kombinieren kann
Wer komplexere UIs baut, der kommt um Drag&Drop oder andere Extrafunktionen mit der Maus nicht herum. Um den Benutzer gut zu unterstützen verwendet man natürlich auch spezielle Cursor.
Auffällig ist aber oft genug der Unterschied zwischen den schönen Standardzeigern im 3D-Stil und den oft flachen Zeigern, die gerade mal zusätzlich ein Plus oder ein Stoppzeichen tragen. Oder ganz und gar der Unterschied , wenn man eigene Cursorstile verwendet, wie z.B. Dirigent oder Hände.
Ich habe mich bei meinem aktuellen Projekt gefragt, warum man eigentlich nicht den aktuellen Standard-Cursor verwendet und diesen zusätzlich mit dem entsprechenden Symbol versieht (Pluszeichen, Stopp, Dragframe etc). Das würde besser aussehen und Arbeit sparen, weil man sich nur noch Gedanken um die Extrasymbolik machen muss und der Benutzer behält seinen eingestellten normalen Cursor.
Schaut man sich den Explorer unter XP+Vista an, dann macht man die Entdeckung, dass der es genauso macht. Der aktuelle Cursor wird beim Drag&Drop Vorgang um ein Stopp- oder Pluszeichen ergänzt.
Warum es also nicht selber auch so machen
Meine Suche im Internet zu diesem Thema: “Wie kombiniert man zwei Cursor zu einem?”, war nicht von Erfolg gekrönt. Beispielcode dazu habe ich nicht gefunden. Aber auch wirklich gar nichts.
Also selbst Hand anlegen.
Bei meinem ersten Versuch wollte ich die Cursor-Bitmaps direkt manipulieren. Bei meiner Untersuchung und dem ersten Democode wurde klar, dass Windows gar keinen Unterschied mehr zwischen Icons und Cursor macht. Erstaunlich
Ob Icon oder Cursor spielt eigentlich keine Rolle. HCURSOR und HICON sind identische Typedefs. Also egal ob toller Farbcursor oder monochromer Cursor, alles gleich.
Mit dieser Entdeckung nahm ich schnell Abstand von BitBlt und Konsorten und verwendete eine Imagelist, die genau diese Funktion des Overlays von Symbolen bereits unterstützt und zudem auch noch perfekt mit Icons umgehen kann.
Herausgekommen ist der folgende Code, der es erlaubt zwei Icons, oder Cursor zu überlagern und ein neues Icon oder einen neuen Cursor zu erzeugen.
Man muss sich aso nur mit LoadCursor, den aktuellen Cursor laden und kann diesen damit ganz einfach modifzieren. Nicht vergessen: Das erzeugte Icon/Cursor muss allerdings mit DestroyIcon auch wieder entsorgt werden. Das Ganze habe ich der Einfachheit halber mit MFC Funktionen geschrieben, aber das Ganze in pure WinAPI zu transformieren dürfte nicht schwer sein.
Hier nun der Code. Have fun!
HICON CombineIcons(HICON hIcon1, HICON hIcon2) { // Remember that HCURSOR and HICON are identical! // hIcon1 is overlayed by hIcon2. // hIcon2 isn't adjusted in size or position. // It just overlays hIcon1 // Get bitmaps of icon 1 ICONINFO iconInfo; ::ZeroMemory(&iconInfo,sizeof(iconInfo)); if (!GetIconInfo(hIcon1,&iconInfo)) return NULL; // Attach the bitmaps to get them automatically freed // upon error. CBitmap bitmap, mask; bitmap.Attach(iconInfo.hbmColor); mask.Attach(iconInfo.hbmMask); // Get size and width BITMAP bm; if (bitmap.m_hObject) bitmap.GetObject(sizeof(bm),&bm); else mask.GetObject(sizeof(bm),&bm); // Get the color depth from the icon and create an image list // Remember we need a UINT flags = 0; switch (bm.bmBitsPixel) { case 4: flags = ILC_COLOR4; break; case 8: flags = ILC_COLOR8; break; case 16: flags = ILC_COLOR16; break; case 24: flags = ILC_COLOR24; break; case 32: flags = ILC_COLOR32; break; default: flags = ILC_COLOR4; break; } CImageList il; // be ware that the monochrom cursor bitmap is twice the height if (!il.Create(bm.bmWidth, bm.bmHeight/(iconInfo.hbmColor!=NULL ? 1 : 2), ILC_MASK|flags,2,2)) return NULL; // Load the both icons into the image list il.Add(hIcon1); il.Add(hIcon2); // Define the second icon as an overlay image il.SetOverlayImage(1,1); // Get a new icon with icon 2 overlayed HICON hCombined = ImageList_GetIcon(il.m_hImageList,0, ILD_NORMAL|INDEXTOOVERLAYMASK(1)); if (!hCombined) return NULL; // Need the icon infos for this new icon ICONINFO iconInfoCombined; ::ZeroMemory(&iconInfoCombined,sizeof(iconInfo)); if (!GetIconInfo(hCombined,&iconInfoCombined)) return NULL; // Destroy the combined icon, we just have the bitmap and the mask ::DestroyIcon(hCombined); // Get the bitmaps into objects to get them automatically freed CBitmap bitmapCombined, maskCombined; bitmapCombined.Attach(iconInfo.hbmColor); maskCombined.Attach(iconInfo.hbmMask); // Get the hotspotinto and cursor data from // the ICONINFO of hCursor1 iconInfoCombined.fIcon = iconInfo.fIcon; iconInfoCombined.xHotspot = iconInfo.xHotspot; iconInfoCombined.yHotspot = iconInfo.yHotspot; // OK we have can create a new Cursor out of the target // Don't forget to use DestroyIcon for the new Cursor/Icon return ::CreateIconIndirect(&iconInfoCombined); }
Themenverwandte Beiträge:
5 Kommentare zu “Aus zwei mach eins: Wie man zwei Cursor kombinieren kann”
Link für diesen Beitrag | RSS-Feed zu diesem Beitrag
Hinterlassen sie einen Kommentar:
Beachten sie bitte, dass Kommentare evtl. nicht sofort hier erscheinen. Die Kommentare werden zur Moderation an den Webmaster gesendet. Es kann also etwas dauern, bis Ihr Kommentar hier veröffentlicht wird!
on Mo 08 Dez 2008 um 10:56 # Carsti
Ja, sowas habe ich auch mal irgendwann gemacht, als ich den Windows-Finder vom Spy++ emulieren mußte. Die ganze Zeit hatte ich Schweißperlen auf der Stirn, weil mir das Wort “Hardware-Cursor” im Kopf rumspukte und ich nicht mehr wußte, welchen Einfluß das hat und ob ich es bei Windows oder nur bei Spielen gehört hatte. Hast Du da mal drüber nachgedacht?
on Mo 08 Dez 2008 um 14:04 # Martin Richter
Also Hardware-Cursor in dem Sinne von “fest-codierten-Cursor-in-der-Grafikkarte” kommen unter Windows nach meinem Wissen schon seit Windows 3.1 nicht mehr zum Einsatz.
Heute ist das nur noch ein fester Speicherbereich in der Grafikkarte, der die Cursor-Anzeige ohne Flackern regelt.
Wie sonst sollten sonst, die verschiedenen Custom-Cursor Varianten funktionieren.
BTW: Mein Code ist getestet und funktioniert auf 3 verschiedenen Laptop-Grafikkarten Varianten und 5 verschiedenen Desktop-Grafikkarten.
Siehe auch:
http://support.microsoft.com/kb/127139/en-us
on Mo 08 Dez 2008 um 14:32 # Carsti
Ich hätte noch nichtmal in meinen kühnsten Träumen auch nur vermutet, daß Du je ungetesteten Code posten würdest. Ich hatte nur von meiner damaligen inneren Unruhe berichtet.
on Fr 28 Aug 2009 um 15:04 # Michel
Hallo Martin, interessanter Beitrag! Aber würde das nicht auch einfach mit ImageList_Merge funktionieren? Oder hat ImageList_Merge Abgründe, von denen man wissen sollte?
on Fr 28 Aug 2009 um 19:26 # Martin Richter
Das würde aber eine neue Imagelist erzeugen und ich müsste das Icon immer noch mit ImageList_GetIcon holen, das kann ich mir sparen indem ich einfach die Overlay Funktion nutze.
Aber ansonsten kommt sicherlich das selbe dabei raus…