MenuStyle.cs
using Sandbox.UI;
namespace Bimp;
/// <summary>
/// A drop down's menu is a popup in the root panel, where a component's stylesheet doesn't reach - it came up in the
/// engine's default look (blue-grey, pale text, square corners) next to our dark rounded remote. Menus opened from our
/// panel get their own sheet, matching the remote: the same dark window and accent, hover and the pad's selection
/// ("pad-selected", see <see cref="PadNavigation"/>) drawn like the rest of our controls, and the current choice marked.
/// </summary>
public static class MenuStyle
{
const string Styles = """
.popup-panel.bimp-menu { background-color: rgba( 14, 17, 23, 0.98 ); border: 1px solid rgba( 255, 255, 255, 0.12 ); border-radius: 8px; box-shadow: 0 10px 40px rgba( 0, 0, 0, 0.6 ); color: white; font-family: Poppins; font-size: 14px; padding: 4px; margin-top: 4px; min-width: 190px; }
.popup-panel.bimp-menu button { padding: 6px 10px; border-radius: 6px; font-weight: 400; color: rgba( 255, 255, 255, 0.85 ); white-space: nowrap; }
.popup-panel.bimp-menu button.current { background-color: rgba( 63, 169, 255, 0.18 ); color: #3fa9ff; font-weight: 600; }
.popup-panel.bimp-menu button:hover { background-color: rgba( 63, 169, 255, 0.3 ); color: white; }
.popup-panel.bimp-menu button.pad-selected { background-color: rgba( 63, 169, 255, 0.3 ); box-shadow: 0 0 0 2px #3fa9ff; }
""";
/// <summary> Call every frame: styles any menu opened from <paramref name="owner"/> that isn't yet. </summary>
public static void Apply( Panel owner )
{
if ( owner is null ) return;
var top = owner;
while ( top.Parent is not null ) top = top.Parent;
foreach ( var menu in top.Descendants.OfType<Popup>() )
{
if ( menu.HasClass( "bimp-menu" ) || !IsInside( menu.PopupSource, owner ) ) continue;
menu.AddClass( "bimp-menu" );
menu.StyleSheet.AddInline( Styles, "bimp-menu" );
// the engine's own "active" mark for the current choice compares the values by reference and misses; ours
// goes by the text the drop down shows
var current = (menu.PopupSource as Button)?.Text;
foreach ( var option in menu.Children )
option.SetClass( "current", option is Button b && b.Text == current );
}
}
static bool IsInside( Panel panel, Panel owner )
{
for ( var p = panel; p is not null; p = p.Parent )
if ( p == owner ) return true;
return false;
}
}