コード例 #1
0
        public void DrawNode(NodeBase node, float itemHeight)
        {
            bool HasText = node.Header != null &&
                           node.Header.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0;

            char icon = IconManager.FOLDER_ICON;

            if (node.Children.Count == 0)
            {
                icon = IconManager.FILE_ICON;
            }
            if (node.Tag is STGenericMesh)
            {
                icon = IconManager.MESH_ICON;
            }
            if (node.Tag is STGenericModel)
            {
                icon = IconManager.MODEL_ICON;
            }

            ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.None;

            flags |= ImGuiTreeNodeFlags.SpanFullWidth;

            if (node.Children.Count == 0 || isSearch)
            {
                flags |= ImGuiTreeNodeFlags.Leaf;
            }
            else
            {
                flags |= ImGuiTreeNodeFlags.OpenOnDoubleClick;
                flags |= ImGuiTreeNodeFlags.OpenOnArrow;
            }

            if (node.IsExpanded && !isSearch)
            {
                flags |= ImGuiTreeNodeFlags.DefaultOpen;
            }

            //Node was selected manually outside the outliner so update the list
            if (node.IsSelected && !SelectedNodes.Contains(node))
            {
                SelectedNodes.Add(node);
            }

            //Node was deselected manually outside the outliner so update the list
            if (!node.IsSelected && SelectedNodes.Contains(node))
            {
                SelectedNodes.Remove(node);
            }

            if (SelectedNodes.Contains(node))
            {
                flags |= ImGuiTreeNodeFlags.Selected;
            }

            if (isSearch && HasText || !isSearch)
            {
                //Add active file format styling. This determines what file to save.
                //For files inside archives, it gets the parent of the file format to save.
                bool isActiveFile = false;
                isActiveFile = ActiveFileFormat == node.Tag;

                bool isRenaming = node == renameNode && isNameEditing && node.Tag is IRenamableNode;

                //Improve tree node spacing.
                var spacing = ImGui.GetStyle().ItemSpacing;
                ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(spacing.X, 1));

                //Make the active file noticable
                if (isActiveFile)
                {
                    ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0.834f, 0.941f, 1.000f, 1.000f));
                }

                //Align the text to improve selection sizing.
                ImGui.AlignTextToFramePadding();

                //Disable selection view in renaming handler to make text more clear
                if (isRenaming)
                {
                    flags &= ~ImGuiTreeNodeFlags.Selected;
                    flags &= ~ImGuiTreeNodeFlags.SpanFullWidth;
                }

                //Load the expander or selection
                if (isSearch)
                {
                    ImGui.Selectable(node.ID, flags.HasFlag(ImGuiTreeNodeFlags.Selected));
                }
                else
                {
                    node.IsExpanded = ImGui.TreeNodeEx(node.ID, flags, $"");
                }

                ImGui.SameLine(); ImGuiHelper.IncrementCursorPosX(3);

                bool leftClicked         = ImGui.IsItemClicked(ImGuiMouseButton.Left);
                bool rightClicked        = ImGui.IsItemClicked(ImGuiMouseButton.Right);
                bool nodeFocused         = ImGui.IsItemFocused();
                bool isToggleOpened      = ImGui.IsItemToggledOpen();
                bool beginDragDropSource = !isRenaming && node.Tag is IDragDropNode && ImGui.BeginDragDropSource();

                if (beginDragDropSource)
                {
                    //Placeholder pointer data. Instead use drag/drop nodes from GetDragDropNode()
                    GCHandle handle1 = GCHandle.Alloc(node.ID);
                    ImGui.SetDragDropPayload("OUTLINER_ITEM", (IntPtr)handle1, sizeof(int), ImGuiCond.Once);
                    handle1.Free();

                    dragDroppedNode = node;

                    //Display icon for texture types
                    if (node.Tag is STGenericTexture)
                    {
                        LoadTextureIcon(node);
                    }

                    //Display text for item being dragged
                    ImGui.Button($"{node.Header}");
                    ImGui.EndDragDropSource();
                }

                bool hasContextMenu = node is IContextMenu || node is IExportReplaceNode || node.Tag is ICheckableNode ||
                                      node.Tag is IContextMenu || node.Tag is IExportReplaceNode ||
                                      node.Tag is STGenericTexture;

                //Apply a pop up menu for context items. Only do this if the menu has possible items used
                if (hasContextMenu && SelectedNodes.Contains(node))
                {
                    ImGui.PushID(node.Header);
                    if (ImGui.BeginPopupContextItem("##OUTLINER_POPUP", ImGuiPopupFlags.MouseButtonRight))
                    {
                        SetupRightClickMenu(node);
                        ImGui.EndPopup();
                    }
                    ImGui.PopID();
                }

                if (node.HasCheckBox)
                {
                    ImGui.SetItemAllowOverlap();

                    ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(2, 2));

                    bool check = node.IsChecked;

                    if (ImGui.Checkbox($"##check{node.ID}", ref check))
                    {
                        foreach (var n in SelectedNodes)
                        {
                            n.IsChecked = check;
                        }
                    }
                    ImGui.PopStyleVar();

                    ImGui.SameLine(); ImGuiHelper.IncrementCursorPosX(3);
                }

                //Load the icon
                if (node.Tag is STGenericTexture)
                {
                    LoadTextureIcon(node);
                }
                else
                {
                    IconManager.DrawIcon(icon);
                    ImGui.SameLine(); ImGuiHelper.IncrementCursorPosX(3);
                }

                ImGui.AlignTextToFramePadding();

                //if (node.Tag is ICheckableNode)
                //  ImGuiHelper.IncrementCursorPosY(-2);

                if (!isRenaming)
                {
                    ImGui.Text(node.Header);
                }
                else
                {
                    var renamable = node.Tag as IRenamableNode;

                    var bg = ImGui.GetStyle().Colors[(int)ImGuiCol.WindowBg];

                    //Make the textbox frame background blend with the tree background
                    //This is so we don't see the highlight color and can see text clearly
                    ImGui.PushStyleColor(ImGuiCol.FrameBg, bg);
                    ImGui.PushStyleColor(ImGuiCol.Border, new Vector4(1, 1, 1, 1));
                    ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1);

                    var length = ImGui.CalcTextSize(renameText).X + 20;
                    ImGui.PushItemWidth(length);

                    if (ImGui.InputText("##RENAME_NODE", ref renameText, 512,
                                        ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CallbackCompletion |
                                        ImGuiInputTextFlags.CallbackHistory | ImGuiInputTextFlags.NoHorizontalScroll))
                    {
                        renamable.Renamed(renameText);
                        node.Header = renameText;

                        isNameEditing = false;
                    }
                    if (!ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Left))
                    {
                        isNameEditing = false;
                    }

                    ImGui.PopItemWidth();
                    ImGui.PopStyleVar();
                    ImGui.PopStyleColor(2);
                }
                ImGui.PopStyleVar();

                if (isActiveFile)
                {
                    ImGui.PopStyleColor();
                }

                if (!isRenaming)
                {
                    //Check for rename selection on selected renamable node
                    if (node.IsSelected && node.Tag is IRenamableNode && RENAME_ENABLE)
                    {
                        bool renameStarting = renameClickTime != 0;
                        bool wasCancelled   = false;

                        //Mouse click before editing started cancels the event
                        if (renameStarting && leftClicked)
                        {
                            renameClickTime = 0;
                            renameStarting  = false;
                            wasCancelled    = true;
                        }

                        //Check for delay
                        if (renameStarting)
                        {
                            //Create a delay between actions. This can be cancelled out during a mouse click
                            var diff = ImGui.GetTime() - renameClickTime;
                            if (diff > RENAME_DELAY_TIME)
                            {
                                //Name edit executed. Setup data for renaming.
                                isNameEditing = true;
                                renameNode    = node;
                                renameText    = ((IRenamableNode)node.Tag).GetRenameText();
                                //Reset the time
                                renameClickTime = 0;
                            }
                        }

                        //User has started a rename click. Start a time check
                        if (leftClicked && renameClickTime == 0 && !wasCancelled)
                        {
                            //Do a small delay for the rename event
                            renameClickTime = ImGui.GetTime();
                        }
                    }

                    //Click event executed on item
                    if ((leftClicked || rightClicked) && !isToggleOpened) //Prevent selection change on toggle
                    {
                        //Reset all selection unless shift/control held down
                        if (!ImGui.GetIO().KeyCtrl&& !ImGui.GetIO().KeyShift)
                        {
                            foreach (var n in SelectedNodes)
                            {
                                n.IsSelected = false;
                            }
                            SelectedNodes.Clear();
                        }

                        //Check selection range
                        if (ImGui.GetIO().KeyShift)
                        {
                            SelectedRangeIndex = node.DisplayIndex;
                            SelectRange        = true;
                        }
                        else
                        {
                            SelectedIndex = node.DisplayIndex;
                        }

                        //Add the clicked node to selection.
                        SelectedNodes.Add(node);
                        node.IsSelected = true;
                    }
                    else if (nodeFocused && !isToggleOpened && !node.IsSelected)
                    {
                        if (!ImGui.GetIO().KeyCtrl&& !ImGui.GetIO().KeyShift)
                        {
                            foreach (var n in SelectedNodes)
                            {
                                n.IsSelected = false;
                            }
                            SelectedNodes.Clear();
                        }

                        //Add the clicked node to selection.
                        SelectedNodes.Add(node);
                        node.IsSelected = true;
                    }

                    if (leftClicked && node.IsSelected)
                    {
                        if (node is ArchiveHiearchy && node.Tag == null)
                        {
                            var archiveWrapper = (ArchiveHiearchy)node;
                            archiveWrapper.OpenFileFormat();
                            archiveWrapper.IsExpanded = true;
                        }
                    }

                    //Update the active file format when selected. (updates dockspace layout and file menus)
                    if (node.Tag is IFileFormat && node.IsSelected)
                    {
                        if (ActiveFileFormat != node.Tag)
                        {
                            ActiveFileFormat = (IFileFormat)node.Tag;
                        }
                    }
                    else if (node.IsSelected && node.Parent != null)
                    {
                    }
                }
            }

            if (isSearch || node.IsExpanded)
            {
                //Todo find a better alternative to clip parents
                //Clip only the last level
                if (ClipNodes && node.Children.Count > 0 && node.Children[0].Children.Count == 0)
                {
                    var children = node.Children.ToList();
                    if (isSearch)
                    {
                        children = GetSearchableNodes(children);
                    }

                    var clipper = new ImGuiListClipper2(children.Count, itemHeight);
                    clipper.ItemsCount = children.Count;

                    for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible items
                    {
                        DrawNode(children[line_i], itemHeight);
                    }
                }
                else
                {
                    foreach (var child in node.Children)
                    {
                        DrawNode(child, itemHeight);
                    }
                }

                if (!isSearch)
                {
                    ImGui.TreePop();
                }
            }
        }
コード例 #2
0
        private unsafe void Render(byte[] mem_data, int mem_size, int base_display_addr = 0)
        {
            float line_height      = ImGuiNative.igGetTextLineHeight();
            int   line_total_count = (mem_size + Rows - 1) / Rows;

            ImGuiNative.igSetNextWindowContentSize(new Vector2(0.0f, line_total_count * line_height));
            ImGui.BeginChild("##scrolling", new Vector2(0, -ImGuiNative.igGetFrameHeightWithSpacing()), false, 0);

            ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(0, 0));
            ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0));

            int addr_digits_count = 0;

            for (int n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
            {
                addr_digits_count++;
            }

            float glyph_width = ImGui.CalcTextSize("F").X;
            float cell_width  = glyph_width * 3; // "FF " we include trailing space in the width to easily catch clicks everywhere

            var clipper            = new ImGuiListClipper2(line_total_count, line_height);
            int visible_start_addr = clipper.DisplayStart * Rows;
            int visible_end_addr   = clipper.DisplayEnd * Rows;

            bool data_next = false;

            if (!AllowEdits || DataEditingAddr >= mem_size)
            {
                DataEditingAddr = -1;
            }

            int data_editing_addr_backup = DataEditingAddr;

            if (DataEditingAddr != -1)
            {
                if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.UpArrow)) && DataEditingAddr >= Rows)
                {
                    DataEditingAddr -= Rows; DataEditingTakeFocus = true;
                }
                else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.DownArrow)) && DataEditingAddr < mem_size - Rows)
                {
                    DataEditingAddr += Rows; DataEditingTakeFocus = true;
                }
                else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.LeftArrow)) && DataEditingAddr > 0)
                {
                    DataEditingAddr -= 1; DataEditingTakeFocus = true;
                }
                else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(ImGuiKey.RightArrow)) && DataEditingAddr < mem_size - 1)
                {
                    DataEditingAddr += 1; DataEditingTakeFocus = true;
                }
            }
            if ((DataEditingAddr / Rows) != (data_editing_addr_backup / Rows))
            {
                // Track cursor movements
                float scroll_offset  = ((DataEditingAddr / Rows) - (data_editing_addr_backup / Rows)) * line_height;
                bool  scroll_desired = (scroll_offset < 0.0f && DataEditingAddr < visible_start_addr + Rows * 2) || (scroll_offset > 0.0f && DataEditingAddr > visible_end_addr - Rows * 2);
                if (scroll_desired)
                {
                    ImGuiNative.igSetScrollYFloat(ImGuiNative.igGetScrollY() + scroll_offset);
                }
            }

            for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible items
            {
                int addr = line_i * Rows;
                ImGui.Text(FixedHex(base_display_addr + addr, addr_digits_count) + ": ");
                ImGui.SameLine();

                // Draw Hexadecimal
                float line_start_x = ImGuiNative.igGetCursorPosX();
                for (int n = 0; n < Rows && addr < mem_size; n++, addr++)
                {
                    ImGui.SameLine(line_start_x + cell_width * n);

                    if (DataEditingAddr == addr)
                    {
                        // Display text input on current byte
                        ImGui.PushID(addr);

                        // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious.
                        ImGuiInputTextCallback callback = (data) =>
                        {
                            int *p_cursor_pos = (int *)data->UserData;

                            if (ImGuiNative.ImGuiInputTextCallbackData_HasSelection(data) == 0)
                            {
                                *p_cursor_pos = data->CursorPos;
                            }
                            return(0);
                        };
                        int  cursor_pos = -1;
                        bool data_write = false;
                        if (DataEditingTakeFocus)
                        {
                            ImGui.SetKeyboardFocusHere();
                            ReplaceChars(DataInput, FixedHex(mem_data[addr], 2));
                            ReplaceChars(AddrInput, FixedHex(base_display_addr + addr, addr_digits_count));
                        }
                        ImGui.PushItemWidth(ImGui.CalcTextSize("FF").X);

                        var flags = ImGuiInputTextFlags.CharsHexadecimal | ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.NoHorizontalScroll | ImGuiInputTextFlags.AlwaysInsertMode | ImGuiInputTextFlags.CallbackAlways;

                        if (ImGui.InputText("##data", DataInput, 32, flags, (IntPtr)(&cursor_pos)))
                        {
                            data_write = data_next = true;
                        }
                        else if (!DataEditingTakeFocus && !ImGui.IsItemActive())
                        {
                            DataEditingAddr = -1;
                        }

                        DataEditingTakeFocus = false;
                        ImGui.PopItemWidth();
                        if (cursor_pos >= 2)
                        {
                            data_write = data_next = true;
                        }
                        if (data_write)
                        {
                            int data;
                            if (TryHexParse(DataInput, out data))
                            {
                                mem_data[addr] = (byte)data;
                            }
                        }
                        ImGui.PopID();
                    }
                    else
                    {
                        ImGui.Text(FixedHex(mem_data[addr], 2));
                        if (AllowEdits && ImGui.IsItemHovered() && ImGui.IsMouseClicked(0))
                        {
                            DataEditingTakeFocus = true;
                            DataEditingAddr      = addr;
                        }
                    }
                }

                ImGui.SameLine(line_start_x + cell_width * Rows + glyph_width * 2);
                //separator line drawing replaced by printing a pipe char

                // Draw ASCII values
                addr = line_i * Rows;
                var asciiVal = new System.Text.StringBuilder(2 + Rows);
                asciiVal.Append("| ");
                for (int n = 0; n < Rows && addr < mem_size; n++, addr++)
                {
                    int c = mem_data[addr];
                    asciiVal.Append((c >= 32 && c < 128) ? Convert.ToChar(c) : '.');
                }
                ImGui.TextUnformatted(asciiVal.ToString());  //use unformatted, so string can contain the '%' character
            }
            //clipper.End();  //not implemented
            ImGui.PopStyleVar(2);

            ImGui.EndChild();

            if (data_next && DataEditingAddr < mem_size)
            {
                DataEditingAddr      = DataEditingAddr + 1;
                DataEditingTakeFocus = true;
            }

            ImGui.Separator();

            ImGuiNative.igAlignTextToFramePadding();
            ImGui.PushItemWidth(50);
            ImGui.PushAllowKeyboardFocus(true);
            int rows_backup = Rows;

            if (ImGui.DragInt("##rows", ref Rows, 0.2f, 4, 32, "%.0f rows"))
            {
                if (Rows <= 0)
                {
                    Rows = 4;
                }
                Vector2 new_window_size = ImGui.GetWindowSize();
                new_window_size.X += (Rows - rows_backup) * (cell_width + glyph_width);
                ImGui.SetWindowSize(new_window_size);
            }
            ImGui.PopAllowKeyboardFocus();
            ImGui.PopItemWidth();
            ImGui.SameLine();
            ImGui.Text(string.Format(" Range {0}..{1} ", FixedHex(base_display_addr, addr_digits_count),
                                     FixedHex(base_display_addr + mem_size - 1, addr_digits_count)));
            ImGui.SameLine();
            ImGui.PushItemWidth(70);
            if (ImGui.InputText("##addr", AddrInput, 32, ImGuiInputTextFlags.CharsHexadecimal | ImGuiInputTextFlags.EnterReturnsTrue))
            {
                int goto_addr;
                if (TryHexParse(AddrInput, out goto_addr))
                {
                    goto_addr -= base_display_addr;
                    if (goto_addr >= 0 && goto_addr < mem_size)
                    {
                        ImGui.BeginChild("##scrolling");
                        ImGui.SetScrollFromPosY(ImGui.GetCursorStartPos().Y + (goto_addr / Rows) * ImGuiNative.igGetTextLineHeight());
                        ImGui.EndChild();
                        DataEditingAddr      = goto_addr;
                        DataEditingTakeFocus = true;
                    }
                }
            }
            ImGui.PopItemWidth();
        }