/********* ** Protected methods *********/ /// <summary>Draw the overlay to the screen.</summary> /// <param name="batch">The sprite batch being drawn.</param> protected override void Draw(SpriteBatch batch) { this.DrawCount++; Rectangle bounds = new Rectangle(this.Menu.xPositionOnScreen, this.Menu.yPositionOnScreen, this.Menu.width, this.Menu.height); // access mode if (!this.ActiveElement.HasFlag(Element.EditForm)) { // tabs this.ChestTab.Draw(batch); this.GroupTab?.Draw(batch); // tab dropdowns if (this.ActiveElement == Element.ChestList) { this.ChestSelector.Draw(batch); } if (this.ActiveElement == Element.GroupList) { this.GroupSelector.Draw(batch); } // edit button this.EditButton.draw(batch); this.SortInventoryButton.draw(batch); } // edit mode else { // get initial measurements SpriteFont font = Game1.smallFont; const int gutter = 10; int padding = Game1.pixelZoom * 10; float topOffset = padding; int maxLabelWidth = (int)new[] { "Location:", "Name:", "Category:", "Order:" }.Select(p => font.MeasureString(p).X).Max(); // background batch.DrawMenuBackground(new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height)); // Location name { string locationName = this.Chest.LocationName; if (this.Chest.Tile != Vector2.Zero) { locationName += $" (tile {this.Chest.Tile.X}, {this.Chest.Tile.Y})"; } Vector2 labelSize = batch.DrawTextBlock(font, "Location:", new Vector2(bounds.X + padding + (int)(maxLabelWidth - font.MeasureString("Location:").X), bounds.Y + topOffset), bounds.Width); batch.DrawTextBlock(font, locationName, new Vector2(bounds.X + padding + maxLabelWidth + gutter, bounds.Y + topOffset), bounds.Width); topOffset += labelSize.Y; } // editable text fields var fields = new[] { Tuple.Create("Name:", this.EditNameField), Tuple.Create("Category:", this.EditCategoryField), Tuple.Create("Order:", this.EditOrderField) }.Where(p => p != null); foreach (var field in fields) { // get data string label = field.Item1; ValidatedTextBox textbox = field.Item2; // draw label Vector2 labelSize = batch.DrawTextBlock(font, label, new Vector2(bounds.X + padding + (int)(maxLabelWidth - font.MeasureString(label).X), bounds.Y + topOffset + 7), bounds.Width); textbox.X = bounds.X + padding + maxLabelWidth + gutter; textbox.Y = bounds.Y + (int)topOffset; textbox.Draw(batch); // update offset topOffset += Math.Max(labelSize.Y + 7, textbox.Height); } // hide chest checkbox { this.EditHideChestField.X = bounds.X + padding; this.EditHideChestField.Y = bounds.Y + (int)topOffset; this.EditHideChestField.Width = 24; this.EditHideChestField.Draw(batch); Vector2 labelSize = batch.DrawTextBlock(font, "Hide this chest" + (this.EditHideChestField.Value ? " (you'll need to find the chest to undo this!)" : ""), new Vector2(bounds.X + padding + 7 + this.EditHideChestField.Width, bounds.Y + topOffset), this.Menu.width, this.EditHideChestField.Value ? Color.Red : Color.Black); topOffset += Math.Max(this.EditHideChestField.Width, labelSize.Y); } // save button this.EditSaveButton.bounds = new Rectangle(bounds.X + padding, bounds.Y + (int)topOffset, this.EditSaveButton.bounds.Width, this.EditSaveButton.bounds.Height); this.EditSaveButton.draw(batch); // exit button this.EditExitButton.draw(batch); } // cursor this.DrawCursor(); }
/********* ** Private methods *********/ /// <summary>Initialise the edit-chest overlay for rendering.</summary> private void ReinitialiseComponents() { Rectangle bounds = new Rectangle(this.Menu.xPositionOnScreen, this.Menu.yPositionOnScreen, this.Menu.width, this.Menu.height); // group dropdown if (this.ShowGroupTab) { // tab Vector2 tabSize = Tab.GetTabSize(this.Font, this.SelectedGroup); this.GroupTab = new Tab(this.SelectedGroup, bounds.Right - (int)tabSize.X - Game1.tileSize, bounds.Y - Game1.pixelZoom * 25, true, this.Font); // dropdown this.GroupSelector = new DropList <string>(this.SelectedGroup, this.Groups, group => group, this.GroupTab.bounds.Right, this.GroupTab.bounds.Bottom, false, this.Font); } // chest dropdown { // tab this.ChestTab = new Tab(this.Chest.Name, bounds.X, bounds.Y - Game1.pixelZoom * 25, true, this.Font); // dropdown ManagedChest[] chests = this.Chests.Where(chest => !this.ShowGroupTab || chest.GetGroup() == this.SelectedGroup).ToArray(); this.ChestSelector = new DropList <ManagedChest>(this.Chest, chests, chest => chest.Name, this.ChestTab.bounds.X, this.ChestTab.bounds.Bottom, true, this.Font); } // edit chest button overlay (based on chest dropdown position) { Rectangle sprite = Sprites.Icons.SpeechBubble; float zoom = Game1.pixelZoom / 2f; Rectangle buttonBounds = new Rectangle(this.ChestTab.bounds.X + this.ChestTab.bounds.Width, this.ChestTab.bounds.Y, (int)(sprite.Width * zoom), (int)(sprite.Height * zoom)); this.EditButton = new ClickableTextureComponent("edit-chest", buttonBounds, null, "edit chest", Sprites.Icons.Sheet, sprite, zoom); } // sort inventory button overlay (based on OK button position) { Rectangle sprite = Sprites.Buttons.Organize; ClickableTextureComponent okButton = this.Menu.okButton; float zoom = Game1.pixelZoom; Rectangle buttonBounds = new Rectangle(okButton.bounds.X, (int)(okButton.bounds.Y - sprite.Height * zoom - 5 * zoom), (int)(sprite.Width * zoom), (int)(sprite.Height * zoom)); this.SortInventoryButton = new ClickableTextureComponent("sort-inventory", buttonBounds, null, "sort inventory", Sprites.Icons.Sheet, sprite, zoom); } // edit form int longTextWidth = (int)Game1.smallFont.MeasureString("A sufficiently, reasonably long string").X; this.EditNameField = new ValidatedTextBox(Game1.smallFont, Color.Black, ch => ch != '|') { Width = longTextWidth, Text = this.Chest.Name }; this.EditCategoryField = new ValidatedTextBox(Game1.smallFont, Color.Black, ch => ch != '|') { Width = longTextWidth, Text = this.Chest.Category }; this.EditOrderField = new ValidatedTextBox(Game1.smallFont, Color.Black, char.IsDigit) { Width = (int)Game1.smallFont.MeasureString("9999999").X, Text = this.Chest.Order?.ToString() }; this.EditHideChestField = new Checkbox(this.Chest.IsIgnored); this.EditSaveButton = new ClickableTextureComponent("save-chest", new Rectangle(0, 0, Game1.tileSize, Game1.tileSize), null, "OK", Game1.mouseCursors, Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, IClickableMenu.borderWithDownArrowIndex), 1f); this.EditExitButton = new ClickableTextureComponent(new Rectangle(bounds.Right - 9 * Game1.pixelZoom, bounds.Y - Game1.pixelZoom * 2, Sprites.Icons.ExitButton.Width * Game1.pixelZoom, Sprites.Icons.ExitButton.Height * Game1.pixelZoom), Sprites.Icons.Sheet, Sprites.Icons.ExitButton, Game1.pixelZoom); // adjust menu to fit this.Menu.trashCan.bounds.Y = this.SortInventoryButton.bounds.Y - this.Menu.trashCan.bounds.Height - 2 * Game1.pixelZoom; }
IControl CreateContent(Template template, IDialog <bool> dialog) { var projectLocation = UserSettings.Folder("MostRecentlyUsedFolder") .Convert(v => v.Or(Optional.Some(_fuse.ProjectsDir)), d => d) .AutoInvalidate(TimeSpan.FromMilliseconds(100)); var validatedLocation = projectLocation.Validate(v => v.NativePath, ValidateExistingDirectory); var projectName = Property.Create( Optional.Some(_shell.MakeUnique(projectLocation.Latest().First().Value / "App").Name)); var validatedName = projectName.Validate(v => v.ToString(), DirectoryName.Validate); var possibleProjectName = projectLocation.CombineLatest( projectName, (loc, pro) => { return(loc.SelectMany(l => pro.Select(n => l / n))); }) .Select( d => d.HasValue && Directory.Exists(d.Value.NativePath) ? Optional.Some("Project '" + d.Value.Name + "' already exists in " + d.Value.ContainingDirectory.NativePath) : Optional.None()); return (Layout.Dock() .Bottom(Layout.Dock() .Right( Buttons.DefaultButtonPrimary( text: "Create", cmd: Observable.CombineLatest( projectName, projectLocation, (name, location) => { var projectDirectory = location.SelectMany(l => name.Select(n => l / n)) .Where(d => !_shell.Exists(d)); return Command.Create( isEnabled: projectDirectory.HasValue, action: async() => { var spawnTemplate = new SpawnTemplate(_shell); var resultPath = spawnTemplate.CreateProject(name.Value.ToString(), template, location.Value); var projectPath = resultPath / new FileName(name.Value + ".unoproj"); await Application.OpenDocument(projectPath, showWindow: true); dialog.Close(true); }); }).Switch()) .WithWidth(104)) .Right(Buttons.DefaultButton(text: "Cancel", cmd: Command.Enabled(() => dialog.Close(false))) .WithWidth(104) .WithPadding(right: new Points(16))) .Fill()) .Fill( Layout.StackFromTop( Label.Create("Name:", color: Theme.DefaultText) .WithPadding(LabelThickness), ValidatedTextBox.Create(validatedName) .WithPadding(ControlPadding), Control.Empty .WithHeight(8), Label.Create("Location:", color: Theme.DefaultText) .WithPadding(LabelThickness), Layout.Dock() .Right( Buttons.DefaultButton(text: "Browse", cmd: Command.Enabled(async() => { var directory = await dialog.BrowseForDirectory(await projectLocation.FirstAsync().Or(DirectoryPath.GetCurrentDirectory())); if (directory.HasValue) { projectLocation.Write(directory.Value, save: true); } })) .WithWidth(80) .WithHeight(22) .CenterVertically() .WithPadding(LabelThickness) ) .Fill(ValidatedTextBox.Create(validatedLocation) .WithPadding(ControlPadding)), ErrorLabel.Create(possibleProjectName) .WithPadding(ControlPadding))) .WithPadding(ControlPadding) .WithPadding(ControlPadding) .WithBackground(Theme.PanelBackground)); }