Unity Development-0.3About Unity’s IMGUI

IMGUI:

An immediate mode GUI system usually does not save GUI information in the immediate mode GUI system, and everything depends on the code driver. For example, specify each part of the UI (Button, Label, etc.) and the corresponding position in the form of a function call

if(GUILayout. Button()){}

, and immediately return to you the interactive results and information.

RetainedGUI:

In the retained mode GUI, the information of various components (Button, Text, etc.) set by the user will be retained and used by the system to render to the screen, respond to events, etc. Changing text or location is manipulating information stored somewhere. When the user changes the value, the system saves the change and is queried or called back by the user.

Event:

The IMGUI code will process the current event at runtime, such as: “EventType.MouseDown\EventType.Repaint”. If the user writes the corresponding event processing by himself, it will look like this, and write corresponding callback processing for different events.

By combining:

The actual structure is similar for different buttons, but the corresponding processing for different events is different. At the same time, in order to connect different behaviors under these different events, IMGUI also has the concept of “ControlID”. ControlID is not related to the control, and is mainly used to process events. After each OnGUI call ends, the relevant ID stack information will be cleared, and the ID is only assigned to the corresponding control according to the order. Since the code is executed sequentially, the same ID after that will be corresponding to the control. For example, there will be a corresponding ControlID in GUI.Button, but it will not be accessed by the outside, nor will it be able to get the events processed inside the control.

How to customize controls

//Determine function parameters: ①Rect contains position and Size information, ②GUIContent, content (text or picture) ③GUIStyle GUI presentation style
publicstaticboolMyButton(Rectrect, GUIContentcontent, GUIStylestyle){
    //First get the ControlID. The ControlID in IMGUI is only allocated according to the code execution order, so even if the control is executing an event that we don't care about at this time, such as (Repaint), it needs to register the ControlID, unless it is not registered for any of his events. ControlID, if you only register ControlID when she executes our expected events such as MouseDown, it will cause id confusion
    intcontrolID=GUIUtility.GetControlID(FocusType.Passive);
    //Then judge the event
    switch(Event.current.GetTypeForControl(controlID)){
        caseEventType. Repaint: {
            floatdefaultWidth=10;
            Rectrect=newRector(rect){width=defaultWidth;}
            GUI.DrawTexture(rect, style.normal.background);
            break;
        }
        caseEventType. MouseDown{
            if(rect.Contains(Event.current.mousePosition) & amp; & amp;Event.current.button==0){
                return true;
                //If other operations of the mouse (such as the position of the mouse is transferred to other controls during dragging, you should pay attention to the mouseUp event) during the mouseDown event of this control will not affect other controls until the mouseUp event is triggered, then Can:
                GUIUtility.hotControl=controlID;
                break;
            }
        }
        caseEventType. MouseUp{
            if( GUIUtility. hotControl==controlID)
             GUIUtility.hotControl=0;
            break;
        }
    }
    
    //If the operation of this control will affect the value change
    GUI.changed=true;//Be careful not to set it to false, the code is executed sequentially, which may hide the operation of other controls changing the value
    
    return false;
}

How to store control state

IMGUI provides a simple storage system for the “state object (self-defined state class)” associated with the control, and through the control’s

Associate the corresponding state object instance. Each ControlID can only have one state object. When the editor code is reloaded, the state object will not be serialized (even if it is marked as [Seriallizable]).

//Implement a flashing red after pressing Button2s
?
//Status class:
public classButtonFlashingStateInfo{
    public float mouseDownTime;
    public voidSetMouseDownTime(){
        mouseDownTime=EditorApplication.timeSinceStartUp;
    }
    
    publicboolisFlashing(intcontrolID){
        if(GUIUtility.ControlID!=controlID) return false;
        floatlapsedTime=EditorApplication.timeSinceStartUp-mouseDownTime;
        if(elapsedTime<=2f) return false;
        return
    }
}
?
//Button:
publicstaticboolFlashButton(Rectrect, GUIContentcontent, GUIStylestyle){
     intcontrolID=GUIUtility.GetControlID(FocusType.Native);
     varstate= (FlashingButtonInfo)GUIUtility.GetStateObject(
                                             typeof(FlashingButtonInfo), controlID);
     switch (Event. current. GetTypeForControl(controlID)) {
         caseEventType. Repaint: {
             GUI.color=state.IsFlashing (controlID)?Color.red: Color.white;
             style.Draw(rect, content, controlID);
             break;
         }
         caseEventType. MouseDown: {
             if (rect. Contains (Event. current. mousePosition)
                   & &Event.current.button==0
                   & amp; & amp;GUIUtility. hotControl==0){
                     GUIUtility.hotControl=controlID;
                     state. SetMouseDownTime();
             }
             break;
         }
         caseEventType. MouseUp: {
             if (GUIUtility. hotControl==controlID)
                 GUIUtility.hotControl=0;
             break;
         }
    }
    return GUIUtility. hotControl==controlID;
}

GUIStyle

GUIStyle contains information about the visual properties of GUI elements, such as fonts, colors, layout properties, etc. All of this information stored in GUIStyle is used to calculate the width and height of a given content and draw it on the screen. And it can also handle different situations, such as drawing different styles when the mouse is hovering over, the control is in active state style, and so on.

There are four main ways GUIStyle draws controls:

  • Construct a new GUIStyle new GUIStyle() and set its properties

  • Use the built-in Style style (can be previewed by the style preview tool)

  • Clone the existing Style and modify it new GUIStyle(rootStyle)

  • Use GUISkins

Layout

All control functions in the GUI library contain a Rect parameter, which we use to determine the position and size of the control on the screen. When I use GUIStyle to involve spacing, we may need a lot of calculation work to ensure this spacing value.

However, IMGUI contains a layouting mechanism, which can automatically calculate the appropriate Rect value for us by responding to EventType.Layout.

  1. IMGUI sends events

  1. The control called by the user uses IMGUI layout functions – (GUILayoutUtility.GetRect(), GUILayout.BeginHorizonal/Vertical, etc.) to build a tree for the space in our layout space and the space it needs.

  1. After the tree is built, recursively calculate the actual width, height and relative position of each element.

//GetRect part of the code
//Receive the Layout event
caseEventType. Layout:
    if (style. isHeightDependantOnWidth){
        //Add GUILayoutEntry to GUILayoutGroup (encapsulation of Rect, defining height range, width range, own rect)
        GUILayoutUtility.current.topLevel.Add((GUILayoutEntry) newGUIWordWrapSizer(style, content, options));
    }else{
        Vector2constraints=newVector2(0.0f, 0.0f);
        if (options!=null)
        {
            foreach (GUILayoutOptionoptioninoptions)
            {
                switch (option.type)
                {
                    caseGUILayoutOption.Type.maxWidth:
                        constraints.x= (float) option.value;
                        break;
                    caseGUILayoutOption.Type.maxHeight:
                        constraints.y= (float) option.value;
                        break;
                }
            }
        }
        Vector2vector2=style. CalcSizeWithConstraints(content, constraints);
        vector2.x=Mathf.Ceil(vector2.x);
        vector2.y=Mathf.Ceil(vector2.y);
        GUILayoutUtility.current.topLevel.Add(newGUILayoutEntry(vector2.x, vector2.x, vector2.y, vector2.y, style, options));
    }
returnGUILayoutUtility.kDummyRect;
  1. If it needs to respond to EventType.Repaint or other types of events, the control will call the same IMGUI layout function, but it will directly return the rectangle it calculates. If GUILayoutUtility.GetRect() has been called in LayoutEvent to register a rectangle, and then called again in the repaint event, it actually returns the rectangle that should be used. That is to say, GetRect does two things (I. During the Layout event, it records the Style we want to use to draw some empty content. II. During other events, it returns us a real Rect. And during the Layout The rectangle is not actually used for anything in it, because IMGUI doesn’t know the actual rectangle to return until the LayoutTree is complete), but this also means that the layout calls need to be consistent between the Layout event and other events, otherwise it will get wrong rectangle