github.com/df-mc/dragonfly@v0.9.13/server/player/form/modal.go (about) 1 package form 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7 ) 8 9 // Modal represents a modal form. These forms have a body with text and two buttons at the end, typically one 10 // for Yes and one for No. These buttons may have custom text, but can, unlike with a Menu form, not have 11 // images next to them. 12 type Modal struct { 13 title, body string 14 submittable ModalSubmittable 15 } 16 17 // NewModal creates a new Modal form using the ModalSubmittable passed to handle the output of the form. The 18 // title passed is formatted following the fmt.Sprintln rules. 19 // Default 'yes' and 'no' buttons may be passed by setting the two exported struct fields of the submittable 20 // to YesButton() and NoButton() respectively. 21 func NewModal(submittable ModalSubmittable, title ...any) Modal { 22 t := reflect.TypeOf(submittable) 23 if t.Kind() != reflect.Struct { 24 panic("submittable must be struct") 25 } 26 m := Modal{title: format(title), submittable: submittable} 27 m.verify() 28 return m 29 } 30 31 // YesButton returns a Button which may be used as a default 'yes' button for a modal form. 32 func YesButton() Button { 33 return Button{Text: "gui.yes"} 34 } 35 36 // NoButton returns a Button which may be used as a default 'no' button for a modal form. 37 func NoButton() Button { 38 return Button{Text: "gui.no"} 39 } 40 41 // MarshalJSON ... 42 func (m Modal) MarshalJSON() ([]byte, error) { 43 return json.Marshal(map[string]any{ 44 "type": "modal", 45 "title": m.title, 46 "content": m.body, 47 "button1": m.Buttons()[0].Text, 48 "button2": m.Buttons()[1].Text, 49 }) 50 } 51 52 // WithBody creates a copy of the Modal form and changes its body to the body passed, after which the new Modal 53 // form is returned. The text is formatted following the rules of fmt.Sprintln. 54 func (m Modal) WithBody(body ...any) Modal { 55 m.body = format(body) 56 return m 57 } 58 59 // Title returns the formatted title passed to the menu upon construction using NewModal(). 60 func (m Modal) Title() string { 61 return m.title 62 } 63 64 // Body returns the formatted text in the body passed to the menu using WithBody(). 65 func (m Modal) Body() string { 66 return m.body 67 } 68 69 // SubmitJSON submits a JSON byte slice to the modal form. This byte slice contains a JSON encoded bool in it, 70 // which is used to determine which button was clicked. 71 func (m Modal) SubmitJSON(b []byte, submitter Submitter) error { 72 if b == nil { 73 if closer, ok := m.submittable.(Closer); ok { 74 closer.Close(submitter) 75 } 76 return nil 77 } 78 79 var value bool 80 if err := json.Unmarshal(b, &value); err != nil { 81 return fmt.Errorf("error parsing JSON as bool: %w", err) 82 } 83 if value { 84 m.submittable.Submit(submitter, m.Buttons()[0]) 85 return nil 86 } 87 m.submittable.Submit(submitter, m.Buttons()[1]) 88 return nil 89 } 90 91 // Buttons returns a list of all buttons of the Modal form, which will always be a total of two buttons. 92 func (m Modal) Buttons() []Button { 93 v := reflect.New(reflect.TypeOf(m.submittable)).Elem() 94 v.Set(reflect.ValueOf(m.submittable)) 95 96 buttons := make([]Button, 0, v.NumField()) 97 for i := 0; i < v.NumField(); i++ { 98 field := v.Field(i) 99 if !field.CanSet() { 100 continue 101 } 102 // Each exported field is guaranteed to be of type Button. 103 buttons = append(buttons, field.Interface().(Button)) 104 } 105 return buttons 106 } 107 108 // verify verifies that the Modal form is valid. It checks if exactly two exported fields are present and 109 // ensures that both have the Button type. 110 func (m Modal) verify() { 111 var count int 112 113 v := reflect.New(reflect.TypeOf(m.submittable)).Elem() 114 v.Set(reflect.ValueOf(m.submittable)) 115 116 for i := 0; i < v.NumField(); i++ { 117 if !v.Field(i).CanSet() { 118 continue 119 } 120 if _, ok := v.Field(i).Interface().(Button); !ok { 121 panic("both exported fields must be of the type form.Button") 122 } 123 count++ 124 } 125 if count != 2 { 126 panic("modal form must have exactly two exported fields of the type form.Button") 127 } 128 }