public void SetupExtraButtons(MainMenuWindow window, UBuilder builder, AddButtonsResult toolButtonsResult) { this.name = "TMPE_MainMenu_ExtraPanel"; // Silver background panel this.atlas = TextureUtil.Ingame; this.backgroundSprite = "GenericPanel"; // The panel will be Dark Silver at 50% dark 100% alpha this.color = new Color32(128, 128, 128, 255); this.ResizeFunction( resizeFn: (UResizer r) => { // Step to the right by 4px r.Stack( mode: UStackMode.ToTheRight, spacing: UConst.UIPADDING); r.FitToChildren(); }); // Place two extra buttons (despawn & clear traffic). // Use as many rows as in the other panel. var extraButtonsResult = AddButtonsFromButtonDefinitions( window, parentComponent: this, builder, buttonDefs: EXTRA_BUTTON_DEFS, minRowLength: toolButtonsResult.Layout.Rows == 2 ? 1 : 2); window.ExtraButtonsList = extraButtonsResult.Buttons; }
/// <summary> /// On Screen Display feature: /// Clear, and hide the keybind panel. /// Populate with items, which can be keyboard shortcuts or hardcoded mouse clicks. /// </summary> /// <param name="items">List of <see cref="OsdItem"/> to display.</param> public static void Display(List <OsdItem> items) { MainMenuWindow mainMenu = ModUI.Instance.MainMenu; Hide(); // Deactivate old items, and destroy them. Also remove them from the panel till Unity // is happy to delete them. foreach (Transform c in mainMenu.OnscreenDisplayPanel.transform) { c.gameObject.SetActive(false); UnityEngine.Object.Destroy(c.gameObject); } // mainMenu.KeybindsPanel.transform.DetachChildren(); // Populate the panel with the items var builder = new UBuilder(); foreach (OsdItem item in items) { item.Build( parent: mainMenu.OnscreenDisplayPanel, builder); } if (items.Count > 0 && mainMenu.OnscreenDisplayPanel.GetUIView() != null) { mainMenu.OnscreenDisplayPanel.opacity = 1f; // fully visible, opaque } // Recalculate now UResizer.UpdateControl(mainMenu); }
/// <summary>Create buttons and add them to the given panel UIBuilder.</summary> /// <param name="window">The parent window.</param> /// <param name="parentComponent">The parent panel component to host the buttons.</param> /// <param name="builder">UI builder to use.</param> /// <param name="buttonDefs">The button definitions array.</param> /// <param name="minRowLength">Shortest horizontal row length allowed before breaking new row.</param> /// <returns>A list of created buttons.</returns> private AddButtonsResult AddButtonsFromButtonDefinitions( MainMenuWindow window, UIComponent parentComponent, UBuilder builder, MenuButtonDef[] buttonDefs, int minRowLength) { AddButtonsResult result; result.Buttons = new List <BaseMenuButton>(); // Count the button objects and set their layout result.Layout = new MainMenuLayout(); result.Layout.CountEnabledButtons(buttonDefs); int placedInARow = 0; foreach (MenuButtonDef buttonDef in buttonDefs) { if (!buttonDef.IsEnabledFunc()) { // Skip buttons which are not enabled continue; } // Create and populate the panel with buttons var button = parentComponent.AddUIComponent(buttonDef.ButtonType) as BaseMenuButton; // Count buttons in a row and break the line bool doRowBreak = result.Layout.IsRowBreak(placedInARow, minRowLength); button.ResizeFunction( resizeFn: (UResizer r) => { r.Stack(doRowBreak ? UStackMode.NewRowBelow : UStackMode.ToTheRight); r.Width(UValue.FixedSize(40f)); r.Height(UValue.FixedSize(40f)); }); if (doRowBreak) { placedInARow = 0; result.Layout.Rows++; } else { placedInARow++; } // Also ask each button what sprites they need button.SetupButtonSkin(builder.AtlasBuilder); // Take button classname, split by ".", and the last word becomes the button name string buttonName = buttonDef.ButtonType.ToString().Split('.').Last(); button.name = $"TMPE_MainMenuButton_{buttonName}"; window.ButtonsDict.Add(buttonDef.Mode, button); result.Buttons.Add(button); } return(result); }
/// <summary> /// Sets up two stacked labels: for mode description (what the user sees) and for hint. /// The text is empty but is updated after every mode or view change. /// </summary> public void SetupControls(SpeedLimitsToolWindow window, UBuilder builder, SpeedLimitsTool parentTool) { this.position = Vector3.zero; this.backgroundSprite = "GenericPanel"; this.color = new Color32(64, 64, 64, 255); this.SetPadding(UPadding.Default); this.ResizeFunction( resizeFn: (UResizer r) => { r.Stack( UStackMode.ToTheRight, spacing: UConst.UIPADDING, stackRef: window.modeButtonsPanel_); r.FitToChildren(); }); this.ModeDescriptionLabel = builder.Label_( parent: this, t: string.Empty, stack: UStackMode.Below, processMarkup: true); this.ModeDescriptionLabel.SetPadding( new UPadding(top: 12f, right: 0f, bottom: 0f, left: 0f)); }
/// <summary>Creates a draggable label with current unit (mph or km/h).</summary> /// <param name="builder">The UI builder to use.</param> private void SetupControls_TitleBar(UBuilder builder) { string unitTitle = string.Format( format: "{0} - {1}", Translation.SpeedLimits.Get("Window.Title:Speed Limits"), GlobalConfig.Instance.Main.DisplaySpeedLimitsMph ? Translation.SpeedLimits.Get("Miles per hour") : Translation.SpeedLimits.Get("Kilometers per hour")); // The label will be repositioned to the top of the parent this.windowTitleLabel_ = builder.Label_( parent: this, t: unitTitle, stack: UStackMode.Below); this.dragHandle_ = this.CreateDragHandle(); // On window drag - clamp to screen and then save in the config this.eventPositionChanged += (_, value) => { GlobalConfig.Instance.Main.SpeedLimitsWindowX = (int)value.x; GlobalConfig.Instance.Main.SpeedLimitsWindowY = (int)value.y; if (!queuedClampToScreen) { // Prevent multiple invocations by setting a flag queuedClampToScreen = true; Invoke(eventName: "OnPeriodicClampToScreen", 2.0f); } }; }
public void SetupControls(MainMenuWindow window, UBuilder builder) { this.name = "TMPE_MainMenu_KeybindsPanel"; // the GenericPanel sprite is Light Silver, make it dark this.atlas = TextureUtil.Ingame; this.backgroundSprite = "GenericPanel"; this.color = new Color32(64, 64, 64, 240); this.opacity = GlobalConfig.Instance.Main.KeybindsPanelVisible ? 1f : 0f; this.SetPadding(UPadding.Default); // The keybinds panel belongs to main menu but does not expand it to fit this.ContributeToBoundingBox(false); this.ResizeFunction( resizeFn: (UResizer r) => { r.Stack(mode: UStackMode.NewRowBelow, spacing: UConst.UIPADDING * 2); // As the control technically belongs inside the mainmenu, it will respect // the 4px padding, we want to shift it slightly left to line up with the // main menu panel. r.MoveBy(new Vector2(-UConst.UIPADDING, 0f)); r.FitToChildren(); }); }
/// <summary>Create speeds palette based on the current options choices.</summary> /// <param name="window">Containing <see cref="SpeedLimitsToolWindow"/>.</param> /// <param name="builder">The UI builder to use.</param> /// <param name="parentTool">The tool object.</param> public void SetupControls(SpeedLimitsToolWindow window, UBuilder builder, SpeedLimitsTool parentTool) { this.parentTool_ = parentTool; this.name = GAMEOBJECT_NAME + "_PalettePanel"; this.position = Vector3.zero; this.SetPadding(UPadding.Default); this.ResizeFunction( resizeFn: r => { r.Stack( mode: UStackMode.Below, stackRef: window.modeDescriptionWrapPanel_); r.FitToChildren(); }); bool showMph = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph; // Fill with buttons // [ 10 20 30 ... 120 130 140 0(no limit) ] //----------------------------------------- // the Current Selected Speed is highlighted List <SetSpeedLimitAction> actions = new(); actions.Add(SetSpeedLimitAction.ResetToDefault()); // add: Default actions.AddRange(PaletteGenerator.AllSpeedLimits(SpeedUnit.CurrentlyConfigured)); actions.Add(SetSpeedLimitAction.Unlimited()); // add: Unlimited this.buttonsByNumber_ = new(); this.PaletteButtons.Clear(); foreach (SetSpeedLimitAction action in actions) { SpeedLimitPaletteButton nextButton = this.SetupControls_SpeedPalette_Button( builder: builder, parent: this, parentTool: parentTool, showMph: showMph, actionOnClick: action); this.PaletteButtons.Add(nextButton); // If this is a numbered button, and its a multiple of 10... if (action.Type == SetSpeedLimitAction.ActionType.SetOverride) { int number = (int)(showMph ? action.GuardedValue.Override.GetMph() : action.GuardedValue.Override.GetKmph()); this.buttonsByNumber_.Add(number, nextButton); } else if (action.Type == SetSpeedLimitAction.ActionType.Unlimited) { this.unlimitedButton_ = nextButton; } else if (action.Type == SetSpeedLimitAction.ActionType.ResetToDefault) { this.resetToDefaultButton_ = nextButton; } } }
/// <summary> /// Creates a button with speed value on it, and label under it, showing opposite units. /// Also can be zero (reset to default) and 1000 km/h (unlimited speed button). /// </summary> /// <param name="builder">UI builder.</param> /// <param name="actionOnClick">What happens if clicked.</param> /// <param name="parentTool">Parent speedlimits tool.</param> /// <param name="buttonPanel">Panel where buttons are added to.</param> /// <param name="speedInteger">Integer value of the speed in the selected units.</param> /// <param name="speedValue">Speed value of the button we're creating.</param> /// <returns>The new button.</returns> private SpeedLimitPaletteButton CreatePaletteButton(UBuilder builder, SetSpeedLimitAction actionOnClick, SpeedLimitsTool parentTool, UPanel buttonPanel, int speedInteger, SpeedValue speedValue) { // Helper function to choose text for the button string GetSpeedButtonText() { if (speedInteger == 0) { return("✖"); // Unicode symbol U+2716 Heavy Multiplication X } if (speedValue.GameUnits >= SpeedValue.UNLIMITED) { return("⊘"); // Unicode symbol U+2298 Circled Division Slash } return(speedInteger.ToString()); } var button = builder.Button <SpeedLimitPaletteButton>(parent: buttonPanel); button.text = GetSpeedButtonText(); button.textScale = UIScaler.UIScale; button.textHorizontalAlignment = UIHorizontalAlignment.Center; button.normalBgSprite = button.hoveredBgSprite = "GenericPanel"; button.color = new Color32(128, 128, 128, 240); // button must know what to do with its speed value button.AssignedAction = actionOnClick; // The click events will be routed via the parent tool OnPaletteButtonClicked button.ParentTool = parentTool; button.SetStacking(UStackMode.NewRowBelow); // Width will be overwritten in SpeedLimitPaletteButton.UpdateSpeedLimitButton button.SetFixedSize( new Vector2( SpeedLimitPaletteButton.DEFAULT_WIDTH, SpeedLimitPaletteButton.DEFAULT_HEIGHT)); return(button); }
/// <summary>Populate the window using UIBuilder of the window panel.</summary> /// <param name="builder">The root builder of this window.</param> public void SetupControls(UBuilder builder, SpeedLimitsTool parentTool) { this.parentTool_ = parentTool; // "Speed Limits - Kilometers per Hour" // "Showing speed limit overrides per road segment." // [ Lane/Segment ] [ 10 20 30 40 50 ... 120 130 140 Max Reset] // [ Edit Default ] | | | | | | | | | | | | // [_MPH/KM_______] [___+__+__+__+__+...+___+___+___+___+_____] // Goes first on top of the window SetupControls_TitleBar(builder); // Vertical panel goes under the titlebar SetupControls_ModeButtons(builder); // Text below for "Current mode: " and "Hold Alt, hold Shift, etc..." modeDescriptionWrapPanel_ = builder.Panel <ModeDescriptionPanel>( parent: this, stack: UStackMode.None); modeDescriptionWrapPanel_.SetupControls(window: this, builder, parentTool); // Palette: Goes right of the modebuttons panel palettePanel_ = builder.Panel <PalettePanel>( parent: this, stack: UStackMode.None); palettePanel_.SetupControls(window: this, builder, parentTool); // palette was built for the current configured MPH/KM display this.DisplaySpeedLimitsMph = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph; cursorTooltip_ = builder.Label <UFloatingTooltip>( parent: this, t: string.Empty, stack: UStackMode.None); // this will hide it, and update it after setup is done cursorTooltip_.SetTooltip(t: null, show: false); this.gameObject.AddComponent <CustomKeyHandler>(); // Force buttons resize and show the current speed limit on the palette this.UpdatePaletteButtonsOnClick(); this.FocusWindow(); }
SetupControls_SpeedPalette_Button(UBuilder builder, UIComponent parent, bool showMph, SetSpeedLimitAction actionOnClick, SpeedLimitsTool parentTool) { SpeedValue speedValue = actionOnClick.Type == SetSpeedLimitAction.ActionType.ResetToDefault ? default : actionOnClick.GuardedValue.Override; int speedInteger = showMph ? speedValue.ToMphRounded(RoadSignThemes.MPH_STEP).Mph : speedValue.ToKmphRounded(RoadSignThemes.KMPH_STEP).Kmph; //-------------------------------- // Create vertical combo: // |[ 100 ]| // | "65 mph" | //-------------------------------- // Create a small panel which stacks together with other button panels horizontally var buttonPanel = builder.Panel_(parent: parent); buttonPanel.name = $"{GAMEOBJECT_NAME}_Button_{speedInteger}"; buttonPanel.ResizeFunction( resizeFn: (UResizer r) => { r.Stack(UStackMode.ToTheRight, spacing: 2f); r.FitToChildren(); }); SpeedLimitPaletteButton button = this.CreatePaletteButton( builder, actionOnClick, parentTool, buttonPanel, speedInteger, speedValue); this.CreatePaletteButtonHintLabel(builder, showMph, speedValue, button, buttonPanel); return(button); }
public AddButtonsResult SetupToolButtons(MainMenuWindow window, UBuilder builder) { this.name = "TMPE_MainMenu_ToolPanel"; this.ResizeFunction( (UResizer r) => { r.Stack(mode: UStackMode.Below); r.FitToChildren(); }); // Create 1 or 2 rows of button objects var toolButtonsResult = AddButtonsFromButtonDefinitions( window, parentComponent: this, builder, buttonDefs: TOOL_BUTTON_DEFS, minRowLength: 4); window.ToolButtonsList = toolButtonsResult.Buttons; return(toolButtonsResult); }
private void CreatePaletteButtonHintLabel(UBuilder builder, bool showMph, SpeedValue speedValue, SpeedLimitPaletteButton button, UPanel buttonPanel) { // Other speed unit info label string otherUnit = showMph ? ToKmphPreciseString(speedValue) : ToMphPreciseString(speedValue); // Choose label text under the button string GetSpeedButtonHintText() { if (FloatUtil.NearlyEqual(speedValue.GameUnits, 0.0f)) { return(Translation.SpeedLimits.Get("Palette.Text:Default")); } if (speedValue.GameUnits >= SpeedValue.UNLIMITED) { return(Translation.SpeedLimits.Get("Palette.Text:Unlimited")); } return(otherUnit); } ULabel label = button.AltUnitsLabel = builder.Label_( parent: buttonPanel, t: GetSpeedButtonHintText(), stack: UStackMode.Below); label.width = SpeedLimitPaletteButton.SELECTED_WIDTH; label.textAlignment = UIHorizontalAlignment.Center; label.ContributeToBoundingBox(false); // parent ignore our width }
/// <summary>Create mode buttons panel on the left side.</summary> /// <param name="builder">The UI builder to use.</param> private void SetupControls_ModeButtons(UBuilder builder) { modeButtonsPanel_ = builder.Panel <ModeButtonsPanel>(parent: this); modeButtonsPanel_.SetupControls(window: this, builder); }
public void SetupControls(SpeedLimitsToolWindow window, UBuilder builder) { this.name = GAMEOBJECT_NAME + "_ModesPanel"; void ButtonpanelResizeFn(UResizer r) { r.Stack( mode: UStackMode.NewRowBelow, spacing: UConst.UIPADDING, stackRef: window.windowTitleLabel_); r.FitToChildren(); } this.ResizeFunction(ButtonpanelResizeFn); Vector2 buttonSize = new Vector2(40f, 40f); UITextureAtlas uiAtlas = window.GetUiAtlas(); LookupTable translation = Translation.SpeedLimits; //---------------- // Edit Segments/Lanes mode button //---------------- this.SegmentModeButton = builder.Button <UButton>( parent: this, text: string.Empty, tooltip: translation.Get("Tooltip:Edit segment speed limits"), size: buttonSize, stack: UStackMode.Below); this.SegmentModeButton.atlas = uiAtlas; // Note the atlas is loaded before this skin is created in window.GetUiAtlas() this.SegmentModeButton.Skin = ButtonSkin.CreateSimple( foregroundPrefix: "EditSegments", backgroundPrefix: UConst.MAINMENU_ROUND_BUTTON_BG) .CanActivate(background: false) .CanHover(foreground: false); this.SegmentModeButton.ApplyButtonSkin(); // the onclick handler is set by SpeedLimitsTool outside of this module //---------------- // Edit Lanes mode button //---------------- this.LaneModeButton = builder.Button <UButton>( parent: this, text: string.Empty, tooltip: translation.Get("Tooltip:Edit lane speed limits"), size: buttonSize, stack: UStackMode.ToTheRight); this.LaneModeButton.atlas = uiAtlas; // Note the atlas is loaded before this skin is created in window.GetUiAtlas() this.LaneModeButton.Skin = ButtonSkin .CreateSimple( foregroundPrefix: "EditLanes", backgroundPrefix: UConst.MAINMENU_ROUND_BUTTON_BG) .CanActivate(background: false) .CanHover(foreground: false); this.LaneModeButton.ApplyButtonSkin(); // the onclick handler is set by SpeedLimitsTool outside of this module //---------------- // Edit Defaults mode button //---------------- this.DefaultsModeButton = builder.Button <UButton>( parent: this, text: string.Empty, tooltip: translation.Get("Tooltip:Default speed limits per road type"), size: buttonSize, stack: UStackMode.NewRowBelow); this.DefaultsModeButton.atlas = uiAtlas; // Note the atlas is loaded before this skin is created in window.GetUiAtlas() this.DefaultsModeButton.Skin = ButtonSkin .CreateSimple( foregroundPrefix: "EditDefaults", backgroundPrefix: UConst.MAINMENU_ROUND_BUTTON_BG) .CanActivate(background: false) .CanHover(foreground: false); this.DefaultsModeButton.ApplyButtonSkin(); // the onclick handler is set by SpeedLimitsTool outside of this module //---------------- // MPH/Kmph switch //---------------- bool displayMph = GlobalConfig.Instance.Main.DisplaySpeedLimitsMph; this.ToggleMphButton = builder.Button <MphToggleButton>( parent: this, text: string.Empty, tooltip: displayMph ? translation.Get("Miles per hour") : translation.Get("Kilometers per hour"), size: buttonSize, stack: UStackMode.ToTheRight); this.ToggleMphButton.atlas = uiAtlas; // Note the atlas is loaded before this skin is created in window.GetUiAtlas() this.ToggleMphButton.Skin = ButtonSkin.CreateSimple( foregroundPrefix: "MphToggle", backgroundPrefix: UConst.MAINMENU_ROUND_BUTTON_BG) .CanActivate(background: false) .CanHover(foreground: false); this.ToggleMphButton.ApplyButtonSkin(); // the onclick handler is set by SpeedLimitsTool outside of this module }
/// <summary> /// Create button triples for number of lanes. /// Buttons are linked to lanes later by LaneArrowTool class. /// </summary> /// <param name="builder">The UI Builder.</param> /// <param name="numLanes">How many lane groups.</param> public void SetupControls(UBuilder builder, int numLanes) { Buttons = new List <LaneArrowButton>(); var buttonRowPanel = builder.Panel_(parent: this, stack: UStackMode.NewRowBelow); buttonRowPanel.name = "TMPE_ButtonRow"; buttonRowPanel.SetPadding(UPadding.Default); buttonRowPanel.ResizeFunction((UResizer r) => { r.FitToChildren(); }); // ----------------------------------- // Create a row of button groups // [ Lane 1 ] [ Lane 2 ] [ Lane 3 ] ... // [ [←] [↑] [→] ] [... ] [ ... ] // ----------------------------------- for (var i = 0; i < numLanes; i++) { string buttonName = $"TMPE_LaneArrow_ButtonGroup{i + 1}"; UPanel buttonGroupPanel = builder.Panel_( parent: buttonRowPanel, stack: i == 0 ? UStackMode.Below : UStackMode.ToTheRight); buttonGroupPanel.name = buttonName; buttonGroupPanel.atlas = TextureUtil.Ingame; buttonGroupPanel.backgroundSprite = "GenericPanel"; int i1 = i; // copy of the loop variable, for the resizeFunction below buttonGroupPanel.ResizeFunction((UResizer r) => { r.FitToChildren(); }); buttonGroupPanel.SetPadding(UPadding.Default); // Create a label with "Lane #" title string labelText = Translation.LaneRouting.Get("Format.Label:Lane") + " " + (i + 1); ULabel laneLabel = builder.Label_( parent: buttonGroupPanel, t: labelText); // The label will be repositioned to the top of the parent laneLabel.ResizeFunction(r => { r.Stack(UStackMode.Below); }); // Create and populate the panel with buttons // 3 buttons are created [←] [↑] [→], // The click event is assigned outside in LaneArrowTool.cs foreach (string prefix in new[] { "LaneArrowLeft", "LaneArrowForward", "LaneArrowRight", }) { LaneArrowButton arrowButton = builder.Button <LaneArrowButton>( parent: buttonGroupPanel, text: string.Empty, tooltip: null, size: new Vector2(40f, 40f), stack: prefix == "LaneArrowLeft" ? UStackMode.Below : UStackMode.ToTheRight); arrowButton.atlas = GetAtlas(); arrowButton.Skin = CreateDefaultButtonSkin(); arrowButton.Skin.ForegroundPrefix = prefix; Buttons.Add(arrowButton); } // for each button } // end button loop, for each lane }