Skip to content

Commit 1c079a7

Browse files
Goober5000claude
andauthored
Fix FRED play/media button transparency and disabled state (scp-fs2open#7524)
CButton::SetBitmap blits a bitmap verbatim and does not perform the magic-color substitution MFC applies when loading toolbar bitmaps. So a button bitmap's background -- the legacy C0C0C0 gray baked into play.bmp et al., or a magenta transparency key -- was drawn literally, reading as a dark/colored rectangle instead of matching the real button face (~F0F0F0 on modern Windows), and a disabled bitmap button embossed its whole opaque rectangle into a solid gray square. Add load_button_icon() to management.{cpp,h}, which builds an icon from a button bitmap by keying out a background color into a transparency mask. Driven through BS_ICON/SetIcon, the OS blends it with the button face and grays it correctly when disabled. Route every play/media button through it (event editor, briefing, debriefing, command brief, music player, sound environment). Also add load_btnface_mapped() -- a toolbar-style color remap -- kept as a lighter alternative for always-enabled buttons. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9be8c62 commit 1c079a7

15 files changed

Lines changed: 137 additions & 41 deletions

fred2/briefingeditordlg.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ briefing_editor_dlg::briefing_editor_dlg(CWnd* pParent /*=NULL*/)
8383
m_use_wing = FALSE;
8484
m_use_cargo = FALSE;
8585
//}}AFX_DATA_INIT
86+
m_play_icon = nullptr;
8687
m_voice_id = -1;
8788
m_cur_stage = 0;
8889
m_last_stage = m_cur_icon = m_last_icon = -1;
@@ -237,8 +238,8 @@ void briefing_editor_dlg::create()
237238
for (auto &sm: Spooled_music)
238239
box->AddString(sm.name);
239240

240-
m_play_bm.LoadBitmap(IDB_PLAY);
241-
((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
241+
m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
242+
((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);
242243

243244
m_current_briefing = 0;
244245
Briefing = &Briefings[m_current_briefing];
@@ -1411,7 +1412,7 @@ void briefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
14111412
BOOL briefing_editor_dlg::DestroyWindow()
14121413
{
14131414
Briefing_dialog = nullptr;
1414-
m_play_bm.DeleteObject();
1415+
if (m_play_icon) DestroyIcon(m_play_icon);
14151416
audiostream_close_file(m_voice_id, 0);
14161417
return CDialog::DestroyWindow();
14171418
}

fred2/briefingeditordlg.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class briefing_editor_dlg : public CDialog
7474
BOOL m_use_cargo;
7575
//}}AFX_DATA
7676

77-
CBitmap m_play_bm;
77+
HICON m_play_icon;
7878

7979
// copy view variables
8080
int m_copy_view_set;

fred2/cmdbrief.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "FRED.h"
1414
#include "freddoc.h"
1515
#include "CmdBrief.h"
16+
#include "management.h"
1617
#include "cfile/cfile.h"
1718
#include "sound/audiostr.h"
1819
#include "localization/localize.h"
@@ -36,6 +37,7 @@ cmd_brief_dlg::cmd_brief_dlg(CWnd* pParent /*=NULL*/)
3637
//}}AFX_DATA_INIT
3738

3839
m_wave_id = -1;
40+
m_play_icon = nullptr;
3941
}
4042

4143
void cmd_brief_dlg::DoDataExchange(CDataExchange* pDX)
@@ -74,8 +76,8 @@ BOOL cmd_brief_dlg::OnInitDialog()
7476
last_cmd_brief = NULL;
7577

7678
CDialog::OnInitDialog();
77-
m_play_bm.LoadBitmap(IDB_PLAY);
78-
((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
79+
m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
80+
((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);
7981

8082
update_data();
8183
return TRUE;
@@ -318,7 +320,7 @@ BOOL cmd_brief_dlg::DestroyWindow()
318320
m_wave_id = -1;
319321

320322
update_data();
321-
m_play_bm.DeleteObject();
323+
if (m_play_icon) DestroyIcon(m_play_icon);
322324

323325
// the command briefing is updated whether we close it, click OK, or click Cancel,
324326
// so autosave here instead of in the OK case

fred2/cmdbrief.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class cmd_brief_dlg : public CDialog
3131
CString m_wave_filename;
3232
//}}AFX_DATA
3333

34-
CBitmap m_play_bm;
34+
HICON m_play_icon;
3535

3636
// Overrides
3737
// ClassWizard generated virtual function overrides

fred2/debriefingeditordlg.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "FRED.h"
1414
#include "DebriefingEditorDlg.h"
1515
#include "FREDDoc.h"
16+
#include "management.h"
1617
#include "mission/missionbriefcommon.h"
1718
#include "mission/missionparse.h"
1819
#include "globalincs/linklist.h"
@@ -51,6 +52,7 @@ debriefing_editor_dlg::debriefing_editor_dlg(CWnd* pParent /*=NULL*/)
5152
m_last_stage = -1;
5253
m_voice_id = -1;
5354
select_sexp_node = -1;
55+
m_play_icon = nullptr;
5456
}
5557

5658
void debriefing_editor_dlg::create()
@@ -148,8 +150,8 @@ void debriefing_editor_dlg::OnInitMenu(CMenu* pMenu)
148150
BOOL debriefing_editor_dlg::OnInitDialog()
149151
{
150152
CDialog::OnInitDialog();
151-
m_play_bm.LoadBitmap(IDB_PLAY);
152-
((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
153+
m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
154+
((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);
153155
CComboBox *box;
154156
box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE);
155157

@@ -502,7 +504,7 @@ BOOL debriefing_editor_dlg::DestroyWindow()
502504
{
503505
Debriefing_dialog = nullptr;
504506
audiostream_close_file(m_voice_id, 0);
505-
m_play_bm.DeleteObject();
507+
if (m_play_icon) DestroyIcon(m_play_icon);
506508
return CDialog::DestroyWindow();
507509
}
508510

fred2/debriefingeditordlg.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class debriefing_editor_dlg : public CDialog
3939
int m_current_debriefing;
4040
//}}AFX_DATA
4141

42-
CBitmap m_play_bm;
42+
HICON m_play_icon;
4343

4444
// Overrides
4545
// ClassWizard generated virtual function overrides

fred2/eventeditor.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ event_editor::event_editor(CWnd* pParent /*=NULL*/)
9999
m_log_1st_trigger = 0;
100100
m_log_last_trigger = 0;
101101
m_log_state_change = 0;
102+
m_play_icon = nullptr;
102103
}
103104

104105
void event_editor::DoDataExchange(CDataExchange* pDX)
@@ -203,8 +204,8 @@ BOOL event_editor::OnInitDialog()
203204
MMessage msg;
204205

205206
CDialog::OnInitDialog(); // let the base class do the default work
206-
m_play_bm.LoadBitmap(IDB_PLAY);
207-
((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
207+
m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
208+
((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);
208209

209210
theApp.init_window(&Events_wnd_data, this, 0);
210211
m_event_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
@@ -1522,7 +1523,7 @@ BOOL event_editor::DestroyWindow()
15221523
audiostream_close_file(m_wave_id, 0);
15231524
m_wave_id = -1;
15241525

1525-
m_play_bm.DeleteObject();
1526+
if (m_play_icon) DestroyIcon(m_play_icon);
15261527
return CDialog::DestroyWindow();
15271528
}
15281529

fred2/eventeditor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class event_editor : public CDialog
108108
int m_log_state_change;
109109
//}}AFX_DATA
110110

111-
CBitmap m_play_bm;
111+
HICON m_play_icon;
112112

113113
// Overrides
114114
// ClassWizard generated virtual function overrides

fred2/fred.rc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,7 +1477,7 @@ BEGIN
14771477
PUSHBUTTON "Browse",IDC_BROWSE_AVI,374,194,49,14,0,WS_EX_STATICEDGE
14781478
COMBOBOX IDC_WAVE_FILENAME,303,210,67,110,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
14791479
PUSHBUTTON "Browse",IDC_BROWSE_WAVE,374,210,34,14,0,WS_EX_STATICEDGE
1480-
PUSHBUTTON "IDB_PLAY",IDC_PLAY,410,210,13,14,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
1480+
PUSHBUTTON "IDB_PLAY",IDC_PLAY,410,210,13,14,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
14811481
COMBOBOX IDC_PERSONA_NAME,303,225,67,57,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
14821482
PUSHBUTTON "Update Stuff",IDC_UPDATE,373,226,50,14
14831483
COMBOBOX IDC_MESSAGE_TEAM,304,243,46,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
@@ -1895,7 +1895,7 @@ BEGIN
18951895
EDITTEXT IDC_TEXT,7,72,396,85,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
18961896
EDITTEXT IDC_VOICE,63,164,83,13,ES_AUTOHSCROLL
18971897
PUSHBUTTON "Browse",IDC_BROWSE,150,164,42,14,0,WS_EX_STATICEDGE
1898-
PUSHBUTTON "IDB_PLAY",IDC_PLAY,194,164,28,14,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
1898+
PUSHBUTTON "IDB_PLAY",IDC_PLAY,194,164,28,14,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
18991899
COMBOBOX IDC_BRIEFING_MUSIC,205,18,83,135,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
19001900
COMBOBOX IDC_SUBSTITUTE_BRIEFING_MUSIC,205,34,83,135,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
19011901
CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,190,200,218,WS_EX_CLIENTEDGE
@@ -1957,7 +1957,7 @@ BEGIN
19571957
CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,55,191,260,WS_EX_CLIENTEDGE
19581958
EDITTEXT IDC_VOICE,193,7,68,14,ES_AUTOHSCROLL
19591959
PUSHBUTTON "Browse",IDC_BROWSE,265,7,34,14,0,WS_EX_STATICEDGE
1960-
PUSHBUTTON "IDB_PLAY",IDC_PLAY,311,7,13,14,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
1960+
PUSHBUTTON "IDB_PLAY",IDC_PLAY,311,7,13,14,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
19611961
EDITTEXT IDC_TEXT,204,34,329,143,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
19621962
EDITTEXT IDC_REC_TEXT,204,192,329,73,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
19631963
LTEXT "Text",IDC_STATIC,204,24,15,8
@@ -2003,10 +2003,10 @@ FONT 8, "MS Sans Serif", 0, 0, 0x0
20032003
BEGIN
20042004
LTEXT "Music Files",IDC_STATIC,8,8,42,10
20052005
LISTBOX IDC_MUSIC_LIST,11,18,126,94,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
2006-
PUSHBUTTON "IDB_PLAY",IDC_BUTTON_PLAY_MUSIC,31,118,15,15,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2007-
PUSHBUTTON "IDB_STOP",IDC_BUTTON_STOP_MUSIC,71,118,15,15,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2008-
PUSHBUTTON "IDB_NEXT",IDC_BUTTON_NEXT_MUSIC,51,118,15,15,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2009-
PUSHBUTTON "IDB_PREV",IDC_BUTTON_PREV_MUSIC,11,118,15,15,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2006+
PUSHBUTTON "IDB_PLAY",IDC_BUTTON_PLAY_MUSIC,31,118,15,15,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2007+
PUSHBUTTON "IDB_STOP",IDC_BUTTON_STOP_MUSIC,71,118,15,15,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2008+
PUSHBUTTON "IDB_NEXT",IDC_BUTTON_NEXT_MUSIC,51,118,15,15,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2009+
PUSHBUTTON "IDB_PREV",IDC_BUTTON_PREV_MUSIC,11,118,15,15,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
20102010
CONTROL "Autoplay",IDC_MUSIC_AUTOPLAY,"Button",BS_3STATE | WS_TABSTOP,94,120,40,10
20112011
PUSHBUTTON "Music.tbl",IDC_BUTTON_MUSIC_TBL,50,140,50,14
20122012
END
@@ -2073,7 +2073,7 @@ BEGIN
20732073
PUSHBUTTON "Browse",IDC_BROWSE_ANI,334,184,50,14,0,WS_EX_STATICEDGE
20742074
EDITTEXT IDC_WAVE_FILENAME,263,202,68,14,ES_AUTOHSCROLL
20752075
PUSHBUTTON "Browse",IDC_BROWSE_WAVE,334,202,34,14,0,WS_EX_STATICEDGE
2076-
PUSHBUTTON "IDB_PLAY",IDC_PLAY,371,202,13,14,BS_BITMAP,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
2076+
PUSHBUTTON "IDB_PLAY",IDC_PLAY,371,202,13,14,BS_ICON,WS_EX_TRANSPARENT | WS_EX_STATICEDGE
20772077
DEFPUSHBUTTON "OK",IDOK,334,14,50,14
20782078
PUSHBUTTON "Cancel",IDCANCEL,334,31,50,14
20792079
LTEXT "Briefing Text",IDC_STATIC,7,44,40,8
@@ -2403,7 +2403,7 @@ BEGIN
24032403
LTEXT "range: 0.1 .. 20.0",IDC_STATIC,157,66,62,11,WS_DISABLED
24042404
GROUPBOX "Preview",IDC_SOUND_ENVIRONMENT_TEST,229,50,79,32
24052405
PUSHBUTTON "Browse",IDC_BROWSE_WAVE,258,62,44,15
2406-
PUSHBUTTON "IDB_PLAY",IDC_PLAY,235,62,17,15,BS_BITMAP,WS_EX_TRANSPARENT
2406+
PUSHBUTTON "IDB_PLAY",IDC_PLAY,235,62,17,15,BS_ICON,WS_EX_TRANSPARENT
24072407
END
24082408

24092409
IDD_WARP_PARAMS DIALOGEX 0, 0, 271, 271

fred2/management.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,3 +2657,65 @@ void update_texture_replacements(const char *old_name, const char *new_name)
26572657
strcpy_s(ii->ship_name, new_name);
26582658
}
26592659
}
2660+
2661+
HBITMAP load_btnface_mapped(UINT id)
2662+
{
2663+
std::array<COLORMAP, 5> cmap;
2664+
cmap[0].from = RGB(255, 0, 255); // magenta (transparent background)
2665+
cmap[0].to = GetSysColor(COLOR_BTNFACE);
2666+
cmap[1].from = RGB(192, 192, 192); // dark gray (traditional background)
2667+
cmap[1].to = GetSysColor(COLOR_BTNFACE);
2668+
cmap[2].from = RGB(128, 128, 128);
2669+
cmap[2].to = GetSysColor(COLOR_BTNSHADOW);
2670+
cmap[3].from = RGB(255, 255, 255);
2671+
cmap[3].to = GetSysColor(COLOR_BTNHIGHLIGHT);
2672+
cmap[4].from = RGB(0, 0, 0);
2673+
cmap[4].to = GetSysColor(COLOR_BTNTEXT);
2674+
return CreateMappedBitmap(AfxGetResourceHandle(), id, 0, cmap.data(), sz2i(cmap.size()));
2675+
}
2676+
2677+
HICON load_button_icon(UINT id, COLORREF transparent)
2678+
{
2679+
// Load the resource bitmap as a device-dependent bitmap.
2680+
HBITMAP color = (HBITMAP)::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(id));
2681+
if (color == nullptr)
2682+
return nullptr;
2683+
2684+
BITMAP bm;
2685+
::GetObject(color, sizeof(bm), &bm);
2686+
2687+
HDC screen_dc = ::GetDC(nullptr);
2688+
HDC color_dc = ::CreateCompatibleDC(screen_dc);
2689+
HDC mask_dc = ::CreateCompatibleDC(screen_dc);
2690+
2691+
// Build a 1-bpp mask: pixels matching the transparent key become white (1),
2692+
// everything else black (0). This is the AND mask of the icon.
2693+
HBITMAP mask = ::CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, nullptr);
2694+
HBITMAP old_color = (HBITMAP)::SelectObject(color_dc, color);
2695+
HBITMAP old_mask = (HBITMAP)::SelectObject(mask_dc, mask);
2696+
2697+
::SetBkColor(color_dc, transparent);
2698+
::BitBlt(mask_dc, 0, 0, bm.bmWidth, bm.bmHeight, color_dc, 0, 0, SRCCOPY);
2699+
2700+
// Punch the transparent pixels in the color bitmap to black (D & ~S), so the
2701+
// icon's XOR stage leaves the background untouched where the mask is set.
2702+
::BitBlt(color_dc, 0, 0, bm.bmWidth, bm.bmHeight, mask_dc, 0, 0, 0x00220326 /*DSna: D & ~S*/);
2703+
2704+
::SelectObject(color_dc, old_color);
2705+
::SelectObject(mask_dc, old_mask);
2706+
::DeleteDC(color_dc);
2707+
::DeleteDC(mask_dc);
2708+
::ReleaseDC(nullptr, screen_dc);
2709+
2710+
ICONINFO ii = {};
2711+
ii.fIcon = TRUE;
2712+
ii.hbmMask = mask;
2713+
ii.hbmColor = color;
2714+
HICON icon = ::CreateIconIndirect(&ii);
2715+
2716+
// CreateIconIndirect copies the bitmaps, so the originals can be freed.
2717+
::DeleteObject(mask);
2718+
::DeleteObject(color);
2719+
2720+
return icon;
2721+
}

0 commit comments

Comments
 (0)