summaryrefslogtreecommitdiffstats
path: root/pkg/gui/controllers/list_controller_trait.go
blob: 0edaa0114ede3f6de5fdde5e5a5d1125e0ebe27c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package controllers

import "github.com/jesseduffield/lazygit/pkg/gui/types"

// Embed this into your list controller to get some convenience methods for
// ensuring a single item is selected, etc.

type ListControllerTrait[T comparable] struct {
	c                *ControllerCommon
	context          types.IListContext
	getSelectedItem  func() T
	getSelectedItems func() ([]T, int, int)
}

func NewListControllerTrait[T comparable](
	c *ControllerCommon,
	context types.IListContext,
	getSelected func() T,
	getSelectedItems func() ([]T, int, int),
) *ListControllerTrait[T] {
	return &ListControllerTrait[T]{
		c:                c,
		context:          context,
		getSelectedItem:  getSelected,
		getSelectedItems: getSelectedItems,
	}
}

// Convenience function for combining multiple disabledReason callbacks.
// The first callback to return a disabled reason will be the one returned.
func (self *ListControllerTrait[T]) require(callbacks ...func() *types.DisabledReason) func() *types.DisabledReason {
	return func() *types.DisabledReason {
		for _, callback := range callbacks {
			if disabledReason := callback(); disabledReason != nil {
				return disabledReason
			}
		}

		return nil
	}
}

// Convenience function for enforcing that a single item is selected.
// Also takes callbacks for additional disabled reasons, and passes the selected
// item into each one.
func (self *ListControllerTrait[T]) singleItemSelected(callbacks ...func(T) *types.DisabledReason) func() *types.DisabledReason {
	return func() *types.DisabledReason {
		if self.context.GetList().AreMultipleItemsSelected() {
			return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupported}
		}

		var zeroValue T
		item := self.getSelectedItem()
		if item == zeroValue {
			return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
		}

		for _, callback := range callbacks {
			if reason := callback(item); reason != nil {
				return reason
			}
		}

		return nil
	}
}

// Ensures that at least one item is selected.
func (self *ListControllerTrait[T]) itemRangeSelected(callbacks ...func([]T, int, int) *types.DisabledReason) func() *types.DisabledReason {
	return func() *types.DisabledReason {
		items, startIdx, endIdx := self.getSelectedItems()
		if len(items) == 0 {
			return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
		}

		for _, callback := range callbacks {
			if reason := callback(items, startIdx, endIdx); reason != nil {
				return reason
			}
		}

		return nil
	}
}

func (self *ListControllerTrait[T]) itemsSelected(callbacks ...func([]T) *types.DisabledReason) func() *types.DisabledReason { //nolint:unused
	return func() *types.DisabledReason {
		items, _, _ := self.getSelectedItems()
		if len(items) == 0 {
			return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
		}

		for _, callback := range callbacks {
			if reason := callback(items); reason != nil {
				return reason
			}
		}

		return nil
	}
}

// Passes the selected item to the callback. Used for handler functions.
func (self *ListControllerTrait[T]) withItem(callback func(T) error) func() error {
	return func() error {
		var zeroValue T
		commit := self.getSelectedItem()
		if commit == zeroValue {
			return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
		}

		return callback(commit)
	}
}

func (self *ListControllerTrait[T]) withItems(callback func([]T) error) func() error {
	return func() error {
		items, _, _ := self.getSelectedItems()
		if len(items) == 0 {
			return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
		}

		return callback(items)
	}
}

// like withItems but also passes the start and end index of the selection
func (self *ListControllerTrait[T]) withItemsRange(callback func([]T, int, int) error) func() error {
	return func() error {
		items, startIdx, endIdx := self.getSelectedItems()
		if len(items) == 0 {
			return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
		}

		return callback(items, startIdx, endIdx)
	}
}

// Like withItem, but doesn't show an error message if no item is selected.
// Use this for click actions (it's a no-op to click empty space)
func (self *ListControllerTrait[T]) withItemGraceful(callback func(T) error) func() error {
	return func() error {
		var zeroValue T
		commit := self.getSelectedItem()
		if commit == zeroValue {
			return nil
		}

		return callback(commit)
	}
}

// All controllers must implement this method so we're defining it here for convenience
func (self *ListControllerTrait[T]) Context() types.Context {
	return self.context
}