Exemplo n.º 1
0
        /// <summary>
        /// Generates a <see cref="VerticalScrollContainer"/> containing an item list within the given container.
        /// </summary>
        /// <param name="itemStacks">Item stacks to draw in the list</param>
        /// <param name="scrollPos">List scroll position</param>
        /// <param name="scrollUpdate">Action invoked with the scroll position of the item list when it is drawn</param>
        /// <param name="interactive">Whether the item counts should be modifiable by the user</param>
        private VerticalScrollContainer GenerateItemList(IEnumerable <StackedThings> itemStacks, Vector2 scrollPos, Action <Vector2> scrollUpdate, bool interactive = false)
        {
            // Create a new flex container as our 'column' to hold each element
            VerticalFlexContainer column = new VerticalFlexContainer(0f);

            // Set up a list to hold our item stack rows
            int iterations = 0;

            foreach (StackedThings itemStack in itemStacks)
            {
                // Create an ItemStackRow from this item
                ItemStackRow row = new ItemStackRow(
                    itemStack: itemStack,
                    interactive: interactive,
                    alternateBackground: iterations++ % 2 != 0, // Be careful of the positioning of ++ here, this should increment /after/ the operation
                    onSelectedChanged: _ =>                     // We don't need the value, so we can just assign it to _
                {
                    //Client.Instance.UpdateTradeItems(tradeId, this.itemStacks.SelectMany(stack => stack.GetSelectedThingsAsProto()));
                }
                    );

                // Contain the row within a minimum-height container
                MinimumContainer container = new MinimumContainer(
                    row,
                    minHeight: OFFER_WINDOW_ROW_HEIGHT
                    );

                // Add it to the row list
                column.Add(container);
            }

            // Return the flex container wrapped in a scroll container
            return(new VerticalScrollContainer(column, scrollPos, scrollUpdate));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> containing their offer.
        /// </summary>
        /// <returns><see cref="VerticalFlexContainer"/> containing their offer</returns>
        private VerticalFlexContainer GenerateTheirOffer()
        {
            // Create a flex container as our 'column' to store elements in
            VerticalFlexContainer column = new VerticalFlexContainer(0f);

            // Title
            column.Add(
                new Container(
                    new TextWidget(
                        text: "Phinix_trade_theirOfferLabel".Translate(),
                        anchor: TextAnchor.MiddleCenter
                        ),
                    height: OFFER_WINDOW_TITLE_HEIGHT
                    )
                );

            // Draw their items
            lock (theirOfferCacheLock)
            {
                column.Add(
                    GenerateItemList(
                        itemStacks: theirOfferCache,
                        scrollPos: theirOfferScrollPos,
                        scrollUpdate: newScrollPos => theirOfferScrollPos = newScrollPos
                        )
                    );
            }

            // Return the generated flex container
            return(column);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Generates a <see cref="VerticalScrollContainer"/> containing a series of available trades.
        /// </summary>
        /// <returns></returns>
        private Displayable GenerateTradeRows()
        {
            // Make sure we are online and have active trades before attempting to draw them
            if (!Instance.Online)
            {
                return(new PlaceholderWidget("Phinix_chat_pleaseLogInPlaceholder".Translate()));
            }
            else if (Instance.GetTrades().Count() == 0)
            {
                return(new PlaceholderWidget("Phinix_trade_noActiveTradesPlaceholder".Translate()));
            }

            // Create a column to store everything in
            VerticalFlexContainer column = new VerticalFlexContainer(DEFAULT_SPACING);

            // Get TradeRows for each trade and add them to the column
            string[] tradeIds = Instance.GetTrades();
            for (int i = 0; i < tradeIds.Length; i++)
            {
                column.Add(
                    new TradeRow(
                        tradeId: tradeIds[i],
                        drawAlternateBackground: i % 2 != 0
                        )
                    );
            }

            // Return the generated column wrapped in a scroll container
            return(new VerticalScrollContainer(column, activeTradesScroll, newScrollPos => activeTradesScroll = newScrollPos));
        }
Exemplo n.º 4
0
        public SettingsWindow()
        {
            doCloseX           = true;
            doCloseButton      = false;
            doWindowBackground = true;

            // Create a flex container to hold our settings
            contents = new VerticalFlexContainer(DEFAULT_SPACING);

            // Server details (address and [dis]connect button) container
            contents.Add(
                new ConditionalContainer(
                    childIfTrue: GenerateConnectedServerDetails(),
                    childIfFalse: GenerateDisconnectedServerDetails(),
                    condition: () => Client.Instance.Connected
                    )
                );

            // Display name and preview
            contents.Add(
                new ConditionalContainer(
                    childIfTrue: GenerateEditableDisplayName(),
                    childIfFalse: new BlankWidget(),
                    condition: () => Client.Instance.Online
                    )
                );
        }
Exemplo n.º 5
0
        /// <summary>
        /// Generates an editable display name field, a button to apply the changes, and a preview.
        /// </summary>
        /// <returns><see cref="Displayable"/> containing an editable display name field, a button to apply the changes, and a preview</returns>
        private Displayable GenerateEditableDisplayName()
        {
            // Make the name preview early so we can bind to it's update method
            DynamicTextWidget namePreview = new DynamicTextWidget(
                textCallback: () => "Phinix_settings_displayNamePreview".Translate(Client.Instance.DisplayName).Resolve(),
                wrap: false
                );

            // Create a column to store the editable portion and preview in
            VerticalFlexContainer column = new VerticalFlexContainer();

            // Create a flex container as our 'row' to store the editable name field in
            HorizontalFlexContainer editableRow = new HorizontalFlexContainer();

            // Editable display name text box
            editableRow.Add(
                new TextFieldWidget(
                    initialText: Client.Instance.DisplayName,
                    onChange: newDisplayName =>
            {
                Client.Instance.DisplayName = newDisplayName;
                namePreview.Update();
            }
                    )
                );

            // Set display name button
            editableRow.Add(
                new Container(
                    new ButtonWidget(
                        label: "Phinix_settings_setDisplayNameButton".Translate(),
                        clickAction: () => Client.Instance.UpdateDisplayName(Client.Instance.DisplayName)
                        ),
                    width: DISPLAY_NAME_SET_BUTTON_WIDTH
                    )
                );

            // Wrap the editable portion in a container to enforce height and add it to the column
            column.Add(
                new HeightContainer(
                    child: editableRow,
                    height: ROW_HEIGHT
                    )
                );

            // Display name preview
            column.Add(new HorizontalScrollContainer(namePreview));

            // Return the generated column
            return(column);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> the chat window consisting of a message list, message entry box, and a send button.
        /// </summary>
        private VerticalFlexContainer GenerateChat()
        {
            // Create a flex container to hold the column elements
            VerticalFlexContainer column = new VerticalFlexContainer(DEFAULT_SPACING);

            // Chat message area
            column.Add(
                new ConditionalContainer(
                    childIfTrue: chatMessageList,
                    childIfFalse: new PlaceholderWidget(
                        text: "Phinix_chat_pleaseLogInPlaceholder".Translate()
                        ),
                    condition: () => Instance.Online
                    )
                );

            // Create a flex container to hold the text field and button
            HorizontalFlexContainer messageEntryFlexContainer = new HorizontalFlexContainer();

            // Message entry field
            messageEntryFlexContainer.Add(messageBox);

            // Send button
            messageEntryFlexContainer.Add(
                new WidthContainer(
                    new ButtonWidget(
                        label: "Phinix_chat_sendButton".Translate(),
                        clickAction: sendChatMessage
                        ),
                    width: CHAT_SEND_BUTTON_WIDTH
                    )
                );

            // Add the flex container to the column
            column.Add(
                new HeightContainer(
                    messageEntryFlexContainer,
                    height: CHAT_TEXTBOX_HEIGHT
                    )
                );

            // Return the generated column
            return(column);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> containing a settings button, user search box, and a user list.
        /// </summary>
        private VerticalFlexContainer GenerateRightColumn()
        {
            // Create a flex container to hold the column elements
            VerticalFlexContainer column = new VerticalFlexContainer();

            // Settings button
            column.Add(
                new Container(
                    new ButtonWidget(
                        label: "Phinix_chat_settingsButton".Translate(),
                        clickAction: () => Find.WindowStack.Add(new SettingsWindow())
                        ),
                    height: SETTINGS_BUTTON_HEIGHT
                    )
                );

            // User search box
            column.Add(
                new Container(
                    new TextFieldWidget(
                        initialText: userSearch,
                        onChange: (newText) =>
            {
                userSearch = newText;
                userList.Update();
            }
                        ),
                    height: USER_SEARCH_HEIGHT
                    )
                );

            // User list
            column.Add(
                new ConditionalContainer(
                    childIfTrue: userList,
                    childIfFalse: new PlaceholderWidget(),
                    condition: () => Instance.Online
                    )
                );

            // Return the generated column
            return(column);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Adds each logged in user to a scrollable container.
        /// </summary>
        /// <returns>A <see cref="VerticalScrollContainer"/> containing the user list</returns>
        private VerticalScrollContainer GenerateUserList()
        {
            // Create a flex container to hold the users
            VerticalFlexContainer userListFlexContainer = new VerticalFlexContainer();

            // Add each logged in user to the flex container
            foreach (string uuid in Instance.GetUserUuids(true))
            {
                // Try to get the display name of the user
                if (!Instance.TryGetDisplayName(uuid, out string displayName))
                {
                    displayName = "???";
                }

                // Skip the user if they don't contain the search text
                if (!string.IsNullOrEmpty(userSearch) && !displayName.ToLower().Contains(userSearch.ToLower()))
                {
                    continue;
                }

                // Strip name formatting if the user wishes not to see it
                if (!Instance.ShowNameFormatting)
                {
                    displayName = TextHelper.StripRichText(displayName);
                }

                userListFlexContainer.Add(
                    new ButtonWidget(
                        label: displayName,
                        clickAction: () => DrawUserContextMenu(uuid, displayName),
                        drawBackground: false
                        )
                    );
            }

            // Wrap the flex container in a scroll container
            VerticalScrollContainer verticalScrollContainer = new VerticalScrollContainer(userListFlexContainer, userListScroll, newScrollPos => userListScroll = newScrollPos);

            // Return the scroll container
            return(verticalScrollContainer);
        }
Exemplo n.º 9
0
        public override void DoWindowContents(Rect inRect)
        {
            doCloseX           = true;
            doCloseButton      = false;
            doWindowBackground = true;

            // Create a flex container to hold our settings
            VerticalFlexContainer flexContainer = new VerticalFlexContainer(DEFAULT_SPACING);

            // Server details (address and [dis]connect button) container
            if (Client.Instance.Connected)
            {
                flexContainer.Add(GenerateConnectedServerDetails());
            }
            else
            {
                flexContainer.Add(GenerateDisconnectedServerDetails());
            }

            // Display name and preview
            if (Client.Instance.Online)
            {
                flexContainer.Add(GenerateEditableDisplayName());
                flexContainer.Add(GenerateNamePreview());
            }
            ;

            // Calculate height and constrain the container so we have even row heights with fluid contents
            float contentHeight = 0f;

            foreach (Displayable item in flexContainer.Contents)
            {
                contentHeight += item.IsFluidHeight ? ROW_HEIGHT : item.CalcHeight(inRect.width);
            }
            contentHeight += (flexContainer.Contents.Count - 1) * DEFAULT_SPACING;
            HeightContainer heightContainer = new HeightContainer(flexContainer, contentHeight);

            // Draw the container with 5f padding at the top to avoid clipping with the close button
            // flexContainer.Draw(inRect.BottomPartPixels(inRect.height - 5f));
            heightContainer.Draw(inRect.BottomPartPixels(inRect.height - 5f));
        }
Exemplo n.º 10
0
        /// <summary>
        /// Creates a new <see cref="TradeList"/>.
        /// </summary>
        public TradeList()
        {
            this.tradeRows = new List <TradeRow>();

            // Create the initial layout
            tradeRowsFlexContainer = new VerticalFlexContainer();

            // Wrap the column in a scroll container
            VerticalScrollContainer scrolledColumn = new VerticalScrollContainer(tradeRowsFlexContainer);

            // Make sure we have active trades before attempting to draw them
            ConditionalContainer activeTradesConditional = new ConditionalContainer(
                childIfTrue: scrolledColumn,
                childIfFalse: new PlaceholderWidget("Phinix_trade_noActiveTradesPlaceholder".Translate()),
                condition: () => tradeRowsFlexContainer.Contents.Any()
                );

            // Make sure we are online above all else
            ConditionalContainer onlineConditional = new ConditionalContainer(
                childIfTrue: activeTradesConditional,
                childIfFalse: new PlaceholderWidget("Phinix_chat_pleaseLogInPlaceholder".Translate()),
                condition: () => Client.Instance.Online
                );

            // Save the layout ready for the draw thread
            constructedLayout = onlineConditional;

            // Subscribe to update events
            Client.Instance.OnTradesSynced           += (s, e) => repopulateTradeRows();
            Client.Instance.OnTradeCancelled         += onTradeCompletedOrCancelledHandler;
            Client.Instance.OnTradeCompleted         += onTradeCompletedOrCancelledHandler;
            Client.Instance.OnTradeCreationSuccess   += onTradeCreationSuccessHandler;
            Client.Instance.OnTradeUpdateFailure     += onTradeUpdateHandler;
            Client.Instance.OnTradeUpdateSuccess     += onTradeUpdateHandler;
            Client.Instance.OnUserDisplayNameChanged += onUserDisplayNameChangedHandler;

            // Pre-fill the trade rows
            repopulateTradeRows();
        }
Exemplo n.º 11
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> containing our available items.
        /// </summary>
        private VerticalFlexContainer GenerateAvailableItems()
        {
//            // Set the text anchor
//            TextAnchor oldAnchor = Text.Anchor;
//            Text.Anchor = TextAnchor.MiddleCenter;
//
//            // 'Sort by' label
//            Rect sortByLabelRect = new Rect(
//                x: container.xMin,
//                y: container.yMin,
//                width: Text.CalcSize("Phinix_trade_sortByLabel".Translate()).x,
//                height: SORT_HEIGHT
//            );
//            Widgets.Label(sortByLabelRect, "Phinix_trade_sortByLabel".Translate());
//
//            // Reset the text anchor
//            Text.Anchor = oldAnchor;
//
//            // First sorting preference
//            Rect firstSortButtonRect = new Rect(
//                x: sortByLabelRect.xMax + DEFAULT_SPACING,
//                y: container.yMin,
//                width: SORT_BUTTON_WIDTH,
//                height: SORT_HEIGHT
//            );
//            if (Widgets.ButtonText(firstSortButtonRect, "", active: false))
//            {
//                // TODO: Sorting
//            }
//
//            // Second sorting preference
//            Rect secondSortButtonRect = new Rect(
//                x: firstSortButtonRect.xMax + DEFAULT_SPACING,
//                y: container.yMin,
//                width: SORT_BUTTON_WIDTH,
//                height: SORT_HEIGHT
//            );
//            if (Widgets.ButtonText(secondSortButtonRect, "", active: false))
//            {
//                // TODO: Sorting
//            }

            // Create a new flex container as our 'column' to store everything in
            VerticalFlexContainer column = new VerticalFlexContainer(DEFAULT_SPACING);

            // Create a new flex container as our 'row' to store the search bar in
            HorizontalFlexContainer searchRow = new HorizontalFlexContainer(DEFAULT_SPACING);

            // Spacer to push everything to the right
            searchRow.Add(
                new SpacerWidget()
                );

            // Search label
            searchRow.Add(
                new Container(
                    new TextWidget(
                        text: "Phinix_trade_searchLabel".Translate(),
                        anchor: TextAnchor.MiddleCenter
                        ),
                    width: Text.CalcSize("Phinix_trade_searchLabel".Translate()).x
                    )
                );

            // Search text field
            searchRow.Add(
                new Container(
                    new TextFieldWidget(
                        text: search,
                        onChange: newSearch => search = newSearch
                        ),
                    width: SEARCH_TEXT_FIELD_WIDTH
                    )
                );

            // Add the search row to the main column
            column.Add(
                new Container(
                    searchRow,
                    height: SORT_HEIGHT
                    )
                );

            // Filter the item stacks list for only those containing the search text
            IEnumerable <StackedThings> filteredItemStacks = itemStacks.Where(itemStack =>
            {
                // Make sure the item stack has things in it
                if (itemStack.Things.Count == 0)
                {
                    return(false);
                }

                // Get the first thing from the item stack
                Thing firstThing = itemStack.Things.First();

                // Return whether the first thing's def label matches the search text
                return(firstThing.def.label.ToLower().Contains(search.ToLower()));
            });

            // Stockpile items list
            column.Add(
                GenerateItemList(filteredItemStacks, stockpileItemsScrollPos, newScrollPos => stockpileItemsScrollPos = newScrollPos, true)
                );

            // Return the generated flex container
            return(column);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> with the offer windows and confirmation statuses.
        /// </summary>
        /// <returns><see cref="VerticalFlexContainer"/> with the offer windows and confirmation statuses</returns>
        private VerticalFlexContainer GenerateOffers()
        {
            // Create a new flex container as the main column to store everything in
            VerticalFlexContainer theAllEncompassingColumnOfOmnipotence = new VerticalFlexContainer(DEFAULT_SPACING);

            // Create a new flex container as our 'row' to store the offers and the centre column in
            HorizontalFlexContainer offerRow = new HorizontalFlexContainer(DEFAULT_SPACING);

            // Our offer
            offerRow.Add(
                new Container(
                    GenerateOurOffer(),
                    width: OFFER_WINDOW_WIDTH
                    )
                );

            // Create a new flex container as a 'column' to store the trade arrows and buttons in
            VerticalFlexContainer centreColumn = new VerticalFlexContainer(DEFAULT_SPACING);

            // Arrows
            centreColumn.Add(
                new FittedTextureWidget(
                    texture: ContentFinder <Texture2D> .Get("tradeArrows")
                    )
                );

            // Update button
            centreColumn.Add(
                new Container(
                    new ButtonWidget(
                        label: "Phinix_trade_updateButton".Translate(),
                        clickAction: () =>
            {
                // Do all of this in a new thread to keep the UI running smoothly
                new Thread(() =>
                {
                    try
                    {
                        // Create a new token
                        string token = Guid.NewGuid().ToString();

                        List <Thing> selectedThings = new List <Thing>();
                        lock (itemStacksLock)
                        {
                            // Collect all our things and despawn them all
                            foreach (StackedThings itemStack in itemStacks)
                            {
                                // Pop the selected things from the stack
                                Thing[] things = itemStack.PopSelected().ToArray();

                                // Despawn each spawned thing
                                foreach (Thing thing in things.Where(t => t.Spawned))
                                {
                                    thing.DeSpawn();
                                }

                                // Add them to the selected things list
                                selectedThings.AddRange(things);
                            }
                        }

                        lock (pendingItemStacksLock)
                        {
                            // Add the items to the pending dictionary
                            pendingItemStacks.Add(token, new PendingThings
                            {
                                Things    = selectedThings.ToArray(),
                                Timestamp = DateTime.UtcNow
                            });
                            Log.Message("Added items to pending");
                        }

                        // Get the items we have on offer and splice in the selected items
                        Instance.TryGetItemsOnOffer(tradeId, Instance.Uuid, out IEnumerable <ProtoThing> itemsOnOffer);
                        IEnumerable <ProtoThing> actualOffer = itemsOnOffer.Concat(selectedThings.Select(TradingThingConverter.ConvertThingFromVerse));

                        // Send an update to the server
                        Instance.UpdateTradeItems(tradeId, actualOffer, token);
                        Log.Message("Sent update");
                    }
                    catch (Exception e)
                    {
                        Log.Message(e.ToString());
                    }
                }).Start();
            }),
                    height: TRADE_BUTTON_HEIGHT
                    )
                );

            // Reset button
            centreColumn.Add(
                new Container(
                    new ButtonWidget(
                        label: "Phinix_trade_resetButton".Translate(),
                        clickAction: () =>
            {
                // Try to get our offer
                if (Instance.TryGetItemsOnOffer(tradeId, Instance.Uuid, out IEnumerable <ProtoThing> protoThings))
                {
                    // Convert and drop our items in pods
                    Instance.DropPods(protoThings.Select(TradingThingConverter.ConvertThingFromProto));
                }
                else
                {
                    // Report a failure
                    Instance.Log(new LogEventArgs("Failed to get our offer when resetting trade! Cannot spawn back items!", LogLevel.ERROR));
                }

                // Reset all selected counts to zero
                foreach (StackedThings itemStack in itemStacks)
                {
                    itemStack.Selected = 0;
                }

                // Update trade items
                Instance.UpdateTradeItems(tradeId, new ProtoThing[0]);
            }
                        ),
                    height: TRADE_BUTTON_HEIGHT
                    )
                );

            // Cancel button
            centreColumn.Add(
                new Container(
                    new ButtonWidget(
                        label: "Phinix_trade_cancelButton".Translate(),
                        clickAction: () => Instance.CancelTrade(tradeId)
                        ),
                    height: TRADE_BUTTON_HEIGHT
                    )
                );

            // Add the centre column to the row
            offerRow.Add(centreColumn);

            // Their offer
            offerRow.Add(
                new Container(
                    GenerateTheirOffer(),
                    width: OFFER_WINDOW_WIDTH
                    )
                );

            // Add the offer row to the main column
            theAllEncompassingColumnOfOmnipotence.Add(offerRow);

            // Create a new row to hold the confirmation checkboxes in
            HorizontalFlexContainer offerAcceptanceRow = new HorizontalFlexContainer(DEFAULT_SPACING);

            // Check if the backend has updated before we let the user change their offer checkbox
            if (Instance.TryGetPartyAccepted(tradeId, Instance.Uuid, out bool accepted) && tradeAccepted != accepted)
            {
                // Update the GUI's status to match the backend
                tradeAccepted = accepted;
            }

            // Our confirmation
            // TODO: Ellipsise display name length if it's going to spill over
            offerAcceptanceRow.Add(
                new Container(
                    new CheckboxLabeledWidget(
                        label: ("Phinix_trade_confirmOurTradeCheckbox" + (tradeAccepted ? "Checked" : "Unchecked")).Translate(), // Janky-looking easter egg, just for you
                        isChecked: tradeAccepted,
                        onChange: (newCheckState) =>
            {
                tradeAccepted = newCheckState;
                Instance.UpdateTradeStatus(tradeId, tradeAccepted);
            }
                        ),
                    width: OFFER_WINDOW_WIDTH
                    )
                );

            // Spacer
            offerAcceptanceRow.Add(
                new SpacerWidget()
                );

            // Their confirmation
            // TODO: Ellipsise display name length if it's going to spill over
            Instance.TryGetOtherPartyAccepted(tradeId, out bool otherPartyAccepted);
            offerAcceptanceRow.Add(
                new Container(
                    new CheckboxLabeledWidget(
                        label: ("Phinix_trade_confirmTheirTradeCheckbox" + (otherPartyAccepted ? "Checked" : "Unchecked")).Translate(TextHelper.StripRichText(GetOtherPartyDisplayName())), // Jankier-looking easter egg, just for you
                        isChecked: otherPartyAccepted,
                        onChange: null
                        ),
                    width: OFFER_WINDOW_WIDTH
                    )
                );

            // Add the offer acceptance row to the main column
            theAllEncompassingColumnOfOmnipotence.Add(
                new Container(
                    offerAcceptanceRow,
                    height: SORT_HEIGHT
                    )
                );

            // Return the generated main column
            return(theAllEncompassingColumnOfOmnipotence);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Draws each chat message within a scrollable container.
        /// </summary>
        private AdapterWidget GenerateMessages()
        {
            return(new AdapterWidget(
                       drawCallback: container =>
            {
                // Get all chat messages and convert them to widgets
                ClientChatMessage[] messages = Instance.GetChatMessages();
                ChatMessageWidget[] messageWidgets = messages.Select(message => new ChatMessageWidget(message.SenderUuid, message.Message, message.Timestamp, message.Status)).ToArray();

                // Create a new flex container from our message list
                VerticalFlexContainer chatFlexContainer = new VerticalFlexContainer(messageWidgets, 0f);

                // Set up the scrollable container
                Rect innerContainer = new Rect(
                    x: container.xMin,
                    y: container.yMin,
                    width: container.width - SCROLLBAR_WIDTH,
                    height: chatFlexContainer.CalcHeight(container.width - SCROLLBAR_WIDTH)
                    );

                // Get a copy of the old scroll position
                Vector2 oldChatScroll = new Vector2(chatScroll.x, chatScroll.y);

                // Start scrolling
                Widgets.BeginScrollView(container, ref chatScroll, innerContainer);

                // Draw the flex container
                chatFlexContainer.Draw(innerContainer);

                // Stop scrolling
                Widgets.EndScrollView();

                // Enter the logic to get sticky scrolling to work

                #region Sticky scroll logic

                // Credit to Aze for figuring out how to get the bottom scroll pos
                bool scrolledToBottom = chatScroll.y.Equals(innerContainer.height - container.height);
                bool scrollChanged = !chatScroll.y.Equals(oldChatScroll.y);
                float heightDifference = oldHeight - innerContainer.height;

                if (scrollChanged)
                {
                    if (scrolledToBottom)
                    {
                        // Enable sticky scroll
                        stickyScroll = true;
                    }
                    else
                    {
                        // Not at bottom, disable sticky scroll
                        stickyScroll = false;
                    }
                }
                else if (!heightDifference.Equals(0f))
                {
                    if (stickyScroll || scrollToBottom)
                    {
                        // Scroll to bottom
                        chatScroll.y = innerContainer.height - container.height;
                        scrollToBottom = false;
                    }
                }

                // Update old height for the next pass
                oldHeight = innerContainer.height;

                #endregion
            }
                       ));
        }
Exemplo n.º 14
0
        /// <summary>
        /// Generates a <see cref="VerticalFlexContainer"/> the chat window consisting of a message list, message entry box, and a send button.
        /// </summary>
        private VerticalFlexContainer GenerateChat()
        {
            // Create a flex container to hold the column elements
            VerticalFlexContainer column = new VerticalFlexContainer(DEFAULT_SPACING);

            // Chat message area
            if (Instance.Online)
            {
                column.Add(
                    GenerateMessages()
                    );
            }
            else
            {
                column.Add(
                    new PlaceholderWidget(
                        text: "Phinix_chat_pleaseLogInPlaceholder".Translate()
                        )
                    );
            }

            // Create a flex container to hold the text field and button
            HorizontalFlexContainer messageEntryFlexContainer = new HorizontalFlexContainer();

            // Message entry field
            messageEntryFlexContainer.Add(
                new TextFieldWidget(
                    text: message,
                    onChange: newMessage => message = newMessage
                    )
                );

            // Send button
            messageEntryFlexContainer.Add(
                new WidthContainer(
                    new ButtonWidget(
                        label: "Phinix_chat_sendButton".Translate(),
                        clickAction: () =>
            {
                // Send the message
                if (!string.IsNullOrEmpty(message) && Instance.Online)
                {
                    // TODO: Make chat message 'sent' callback to remove message, preventing removal of lengthy messages for nothing and causing frustration
                    Instance.SendMessage(message);

                    message        = "";
                    scrollToBottom = true;
                }
            }
                        ),
                    width: CHAT_SEND_BUTTON_WIDTH
                    )
                );

            // Add the flex container to the column
            column.Add(
                new HeightContainer(
                    messageEntryFlexContainer,
                    height: CHAT_TEXTBOX_HEIGHT
                    )
                );

            // Return the generated column
            return(column);
        }