Add default selection to MultiItemSelector

- Added default selection to items
- Added correct styling for disabled but default selected item
- Added missing license header
- Added missing javadoc
- Added default selection to sample/doc code
This commit is contained in:
Drevsh
2022-07-20 08:15:19 +01:00
committed by Janne Valkealahti
parent 329c7eb559
commit 2862cc855e
12 changed files with 92 additions and 24 deletions

View File

@@ -32,6 +32,7 @@ import org.springframework.shell.component.support.Enableable;
import org.springframework.shell.component.support.Itemable;
import org.springframework.shell.component.support.Matchable;
import org.springframework.shell.component.support.Nameable;
import org.springframework.shell.component.support.Selectable;
import org.springframework.shell.component.support.AbstractSelectorComponent.SelectorComponentContext;
import org.springframework.shell.component.MultiItemSelector.MultiItemSelectorContext;
@@ -40,7 +41,7 @@ import org.springframework.shell.component.MultiItemSelector.MultiItemSelectorCo
*
* @author Janne Valkealahti
*/
public class MultiItemSelector<T, I extends Nameable & Matchable & Enableable & Itemable<T>>
public class MultiItemSelector<T, I extends Nameable & Matchable & Enableable & Selectable & Itemable<T>>
extends AbstractSelectorComponent<T, MultiItemSelectorContext<T, I>, I> {
private MultiItemSelectorContext<T, I> currentContext;

View File

@@ -32,6 +32,7 @@ import org.springframework.shell.component.support.Enableable;
import org.springframework.shell.component.support.Itemable;
import org.springframework.shell.component.support.Matchable;
import org.springframework.shell.component.support.Nameable;
import org.springframework.shell.component.support.Selectable;
import org.springframework.shell.component.support.AbstractSelectorComponent.SelectorComponentContext;
import org.springframework.shell.component.SingleItemSelector.SingleItemSelectorContext;
@@ -40,7 +41,7 @@ import org.springframework.shell.component.SingleItemSelector.SingleItemSelector
*
* @author Janne Valkealahti
*/
public class SingleItemSelector<T, I extends Nameable & Matchable & Enableable & Itemable<T>>
public class SingleItemSelector<T, I extends Nameable & Matchable & Enableable & Selectable & Itemable<T>>
extends AbstractSelectorComponent<T, SingleItemSelectorContext<T, I>, I> {
private SingleItemSelectorContext<T, I> currentContext;

View File

@@ -617,7 +617,7 @@ public interface ComponentFlow {
private Stream<OrderedInputOperation> multiItemSelectorsStream() {
return multiInputs.stream().map(input -> {
List<SelectorItem<String>> selectorItems = input.getSelectItems().stream()
.map(si -> SelectorItem.of(si.name(), si.item(), si.enabled()))
.map(si -> SelectorItem.of(si.name(), si.item(), si.enabled(), si.selected()))
.collect(Collectors.toList());
MultiItemSelector<String, SelectorItem<String>> selector = new MultiItemSelector<>(terminal,
selectorItems, input.getName(), input.getComparator());

View File

@@ -25,11 +25,13 @@ public class DefaultSelectItem implements SelectItem {
private String name;
private String item;
private boolean enabled;
private boolean selected;
public DefaultSelectItem(String name, String item, boolean enabled) {
public DefaultSelectItem(String name, String item, boolean enabled, boolean selected) {
this.name = name;
this.item = item;
this.enabled = enabled;
this.selected = selected;
}
@Override
@@ -46,4 +48,9 @@ public class DefaultSelectItem implements SelectItem {
public boolean enabled() {
return enabled;
}
@Override
public boolean selected() {
return selected;
}
}

View File

@@ -43,11 +43,18 @@ public interface SelectItem {
*/
boolean enabled();
/**
* Return if the item is selected.
*
* @return if item is selected
*/
boolean selected();
public static SelectItem of(String name, String item) {
return of(name, item, true);
return of(name, item, true, false);
}
public static SelectItem of(String name, String item, boolean enabled) {
return new DefaultSelectItem(name, item, enabled);
public static SelectItem of(String name, String item, boolean enabled, boolean selected) {
return new DefaultSelectItem(name, item, enabled, selected);
}
}

View File

@@ -45,7 +45,7 @@ import static org.jline.keymap.KeyMap.key;
*
* @author Janne Valkealahti
*/
public abstract class AbstractSelectorComponent<T, C extends SelectorComponentContext<T, I, C>, I extends Nameable & Matchable & Enableable & Itemable<T>>
public abstract class AbstractSelectorComponent<T, C extends SelectorComponentContext<T, I, C>, I extends Nameable & Matchable & Enableable & Selectable & Itemable<T>>
extends AbstractComponent<C> {
private final static Logger log = LoggerFactory.getLogger(AbstractSelectorComponent.class);
@@ -257,7 +257,7 @@ public abstract class AbstractSelectorComponent<T, C extends SelectorComponentCo
AtomicInteger index = new AtomicInteger(0);
itemStates = context.getItems().stream()
.sorted(comparator)
.map(item -> ItemState.of(item, item.getName(), index.getAndIncrement(), item.isEnabled()))
.map(item -> ItemState.of(item, item.getName(), index.getAndIncrement(), item.isEnabled(), item.isSelected()))
.collect(Collectors.toList());
}
for (int i = 0; i < itemStates.size(); i++) {
@@ -280,7 +280,7 @@ public abstract class AbstractSelectorComponent<T, C extends SelectorComponentCo
AtomicInteger index = new AtomicInteger(0);
itemStates = context.getItems().stream()
.sorted(comparator)
.map(item -> ItemState.of(item, item.getName(), index.getAndIncrement(), item.isEnabled()))
.map(item -> ItemState.of(item, item.getName(), index.getAndIncrement(), item.isEnabled(), item.isSelected()))
.collect(Collectors.toList());
context.setItemStates(itemStates);
}
@@ -547,11 +547,12 @@ public abstract class AbstractSelectorComponent<T, C extends SelectorComponentCo
boolean enabled;
int index;
ItemState(I item, String name, int index, boolean enabled) {
ItemState(I item, String name, int index, boolean enabled, boolean selected) {
this.item = item;
this.name = name;
this.index = index;
this.enabled = enabled;
this.selected = selected;
}
public boolean matches(String match) {
@@ -574,8 +575,8 @@ public abstract class AbstractSelectorComponent<T, C extends SelectorComponentCo
return enabled;
}
static <I extends Matchable> ItemState<I> of(I item, String name, int index, boolean enabled) {
return new ItemState<I>(item, name, index, enabled);
static <I extends Matchable> ItemState<I> of(I item, String name, int index, boolean enabled, boolean selected) {
return new ItemState<I>(item, name, index, enabled, selected);
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.component.support;
public interface Selectable {
boolean isSelected();
}

View File

@@ -17,29 +17,31 @@ package org.springframework.shell.component.support;
import org.springframework.util.StringUtils;
public interface SelectorItem<T> extends Nameable, Matchable, Enableable, Itemable<T> {
public interface SelectorItem<T> extends Nameable, Matchable, Enableable, Selectable, Itemable<T> {
static <T> SelectorItem<T> of(String name, T item) {
return of(name, item, true);
return of(name, item, true, false);
}
static <T> SelectorItem<T> of(String name, T item, boolean enabled) {
return new SelectorItemWrapper<T>(name, item, enabled);
static <T> SelectorItem<T> of(String name, T item, boolean enabled, boolean selected) {
return new SelectorItemWrapper<T>(name, item, enabled, selected);
}
public static class SelectorItemWrapper<T> implements SelectorItem<T> {
private String name;
private boolean enabled;
private T item;
private boolean selected;
public SelectorItemWrapper(String name, T item) {
this(name, item, true);
this(name, item, true, false);
}
public SelectorItemWrapper(String name, T item, boolean enabled) {
public SelectorItemWrapper(String name, T item, boolean enabled, boolean selected) {
this.name = name;
this.item = item;
this.enabled = enabled;
this.selected = selected;
}
@Override
@@ -51,7 +53,7 @@ public interface SelectorItem<T> extends Nameable, Matchable, Enableable, Itemab
public boolean matches(String match) {
if (!StringUtils.hasText(match)) {
return true;
};
}
return name.toLowerCase().contains(match.toLowerCase());
}
@@ -60,8 +62,14 @@ public interface SelectorItem<T> extends Nameable, Matchable, Enableable, Itemab
return enabled;
}
@Override
public T getItem() {
return item;
}
@Override
public boolean isSelected() {
return selected;
}
}
}

View File

@@ -18,7 +18,11 @@ select_item(item) ::= <%
<({<figures.checkboxOff> }); format=selected_style(item.selected)> <item.name>
<endif>
<else>
<({<figures.checkboxOff> }); format="style-item-disabled"> <item.name; format="style-item-disabled">
<if(item.selected)>
<({<figures.checkboxOn> }); format="style-item-disabled"> <item.name; format="style-item-disabled">
<else>
<({<figures.checkboxOff> }); format="style-item-disabled"> <item.name; format="style-item-disabled">
<endif>
<endif>
%>

View File

@@ -51,13 +51,15 @@ public class MultiItemSelectorTests extends AbstractShellTests {
private static SimplePojo SIMPLE_POJO_5 = SimplePojo.of("data5");
private static SimplePojo SIMPLE_POJO_6 = SimplePojo.of("data6");
private static SimplePojo SIMPLE_POJO_7 = SimplePojo.of("data7");
private static SimplePojo SIMPLE_POJO_8 = SimplePojo.of("data8");
private static SelectorItem<SimplePojo> SELECTOR_ITEM_1 = SelectorItem.of("simplePojo1", SIMPLE_POJO_1);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_2 = SelectorItem.of("simplePojo2", SIMPLE_POJO_2);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_3 = SelectorItem.of("simplePojo3", SIMPLE_POJO_3);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_4 = SelectorItem.of("simplePojo4", SIMPLE_POJO_4);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_5 = SelectorItem.of("simplePojo5", SIMPLE_POJO_5);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_6 = SelectorItem.of("simplePojo6", SIMPLE_POJO_6);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_7 = SelectorItem.of("simplePojo7", SIMPLE_POJO_7, false);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_7 = SelectorItem.of("simplePojo7", SIMPLE_POJO_7, false, false);
private static SelectorItem<SimplePojo> SELECTOR_ITEM_8 = SelectorItem.of("simplePojo8", SIMPLE_POJO_8, false, true);
private ExecutorService service;
private CountDownLatch latch;
@@ -204,6 +206,22 @@ public class MultiItemSelectorTests extends AbstractShellTests {
Stream<String> datas = selected.stream().map(SelectorItem::getItem).map(SimplePojo::getData);
assertThat(datas).containsExactlyInAnyOrder("data4");
}
@Test
public void testDefaultSelection() throws InterruptedException {
scheduleSelect(Arrays.asList(SELECTOR_ITEM_1, SELECTOR_ITEM_2, SELECTOR_ITEM_7, SELECTOR_ITEM_8));
TestBuffer testBuffer = new TestBuffer().cr();
write(testBuffer.getBytes());
awaitLatch();
List<SelectorItem<SimplePojo>> selected = result.get();
assertThat(selected).hasSize(1);
Stream<String> datas = selected.stream().map(SelectorItem::getItem).map(SimplePojo::getData);
assertThat(datas).containsExactlyInAnyOrder("data8");
assertThat(consoleOut()).contains("testSimple data8");
}
private void scheduleSelect() {
scheduleSelect(Arrays.asList(SELECTOR_ITEM_1, SELECTOR_ITEM_2, SELECTOR_ITEM_3,

View File

@@ -170,7 +170,7 @@ public class UiComponentSnippets {
public String multiSelector() {
List<SelectorItem<String>> items = new ArrayList<>();
items.add(SelectorItem.of("key1", "value1"));
items.add(SelectorItem.of("key2", "value2", false));
items.add(SelectorItem.of("key2", "value2", false, true));
items.add(SelectorItem.of("key3", "value3"));
MultiItemSelector<String, SelectorItem<String>> component = new MultiItemSelector<>(getTerminal(),
items, "testSimple", null);

View File

@@ -93,7 +93,7 @@ public class ComponentCommands extends AbstractShellComponent {
public String multiSelector() {
List<SelectorItem<String>> items = new ArrayList<>();
items.add(SelectorItem.of("key1", "value1"));
items.add(SelectorItem.of("key2", "value2", false));
items.add(SelectorItem.of("key2", "value2", false, true));
items.add(SelectorItem.of("key3", "value3"));
MultiItemSelector<String, SelectorItem<String>> component = new MultiItemSelector<>(getTerminal(),
items, "testSimple", null);