Initial DialogView

- Relates #855
This commit is contained in:
Janne Valkealahti
2023-08-21 09:05:35 +03:00
parent 50806589bc
commit c667b8e7b2
2 changed files with 243 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2023 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.view.control;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import org.springframework.shell.component.view.control.ButtonView.ButtonViewSelectEvent;
import org.springframework.shell.component.view.event.EventLoop;
import org.springframework.shell.component.view.event.MouseHandler;
import org.springframework.shell.component.view.event.MouseHandler.MouseHandlerResult;
import org.springframework.shell.component.view.geom.Rectangle;
import org.springframework.shell.component.view.message.ShellMessageBuilder;
import org.springframework.shell.component.view.screen.Screen;
import org.springframework.shell.component.view.screen.Screen.Writer;
/**
* {@code DialogView} is a {@link View} with border, number of buttons and area
* for a generic content.
*
* @author Janne Valkealahti
*/
public class DialogView extends BoxView {
private View content;
private List<ButtonView> buttons;
public DialogView() {
this(null);
}
public DialogView(View content, ButtonView... buttons) {
this(content, Arrays.asList(buttons));
}
public DialogView(View content, List<ButtonView> buttons) {
this.content = content;
this.buttons = buttons;
}
@Override
public void setEventLoop(EventLoop eventLoop) {
// TODO: should find better way to hook into eventloop
super.setEventLoop(eventLoop);
hookButtonEvents();
}
private void hookButtonEvents() {
buttons.forEach(b -> {
onDestroy(getEventLoop().viewEvents(ButtonViewSelectEvent.class, b)
.subscribe(event -> {
dispatch();
ViewService viewService = getViewService();
if (viewService != null) {
viewService.setModal(null);
}
}));
});
}
@Override
public MouseHandler getMouseHandler() {
return args -> {
View focus = null;
for (ButtonView b : buttons) {
MouseHandlerResult r = b.getMouseHandler().handle(args);
if (r.focus() != null) {
focus = r.focus();
break;
}
}
return MouseHandler.resultOf(args.event(), true, focus, null);
};
}
@Override
public void setRect(int x, int y, int width, int height) {
super.setRect(x, y, width, height);
Rectangle rect = getInnerRect();
rect = new Rectangle(rect.x() + rect.width() / 4, rect.y() + rect.height() / 4, rect.width() / 2, rect.height() / 2);
rect = new Rectangle(rect.x() + 1, rect.y() + 1, rect.width() - 2, rect.height() - 2);
if (content != null) {
content.setRect(rect.x(), rect.y(), rect.width(), rect.height() - 3);
content.setLayer(getLayer());
}
int xx = rect.x();
ListIterator<ButtonView> iter = buttons.listIterator();
while (iter.hasNext()) {
ButtonView button = iter.next();
button.setLayer(getLayer());
button.setRect(xx, rect.y() + rect.height() - 3, 7, 3);
xx += 7;
}
}
@Override
protected void drawInternal(Screen screen) {
Rectangle rect = getInnerRect();
rect = new Rectangle(rect.x() + rect.width() / 4, rect.y() + rect.height() / 4, rect.width() / 2, rect.height() / 2);
Writer writer = screen.writerBuilder().layer(getLayer()).build();
writer.border(rect.x(), rect.y(), rect.width(), rect.height());
if (content != null) {
content.draw(screen);
}
ListIterator<ButtonView> iter = buttons.listIterator();
while (iter.hasNext()) {
ButtonView button = iter.next();
button.draw(screen);
}
super.drawInternal(screen);
}
private void dispatch() {
dispatch(ShellMessageBuilder.ofView(this, DialogViewCloseEvent.of(this)));
}
public record DialogViewItemEventArgs() implements ViewEventArgs {
public static DialogViewItemEventArgs of() {
return new DialogViewItemEventArgs();
}
}
public record DialogViewCloseEvent(View view, DialogViewItemEventArgs args) implements ViewEvent {
public static DialogViewCloseEvent of(View view) {
return new DialogViewCloseEvent(view, DialogViewItemEventArgs.of());
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2023 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.view.control;
import java.time.Duration;
import java.util.Arrays;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.shell.component.view.control.ButtonView.ButtonViewSelectEvent;
import org.springframework.shell.component.view.control.DialogView.DialogViewCloseEvent;
import org.springframework.shell.component.view.event.MouseEvent;
import org.springframework.shell.component.view.event.MouseHandler.MouseHandlerResult;
import static org.assertj.core.api.Assertions.assertThat;
class DialogViewTests extends AbstractViewTests {
private DialogView view;
@Nested
class Construction {
@Test
void constructView() {
view = new DialogView();
ButtonView button = new ButtonView("text");
view = new DialogView(new BoxView(), Arrays.asList(button));
}
}
@Nested
class Events {
@BeforeEach
void setup() {
ButtonView button = new ButtonView("text");
configure(button);
view = new DialogView(null, button);
configure(view);
view.setRect(0, 0, 10, 10);
}
@Test
void handlesMouseClick() {
MouseEvent click = mouseClick(3, 3);
Flux<ButtonViewSelectEvent> actions1 = eventLoop
.viewEvents(ButtonViewSelectEvent.class);
Flux<DialogViewCloseEvent> actions2 = eventLoop
.viewEvents(DialogViewCloseEvent.class);
StepVerifier verifier1 = StepVerifier.create(actions1)
.expectNextCount(1)
.thenCancel()
.verifyLater();
StepVerifier verifier2 = StepVerifier.create(actions2)
.expectNextCount(1)
.thenCancel()
.verifyLater();
MouseHandlerResult result = handleMouseClick(view, click);
assertThat(result).isNotNull().satisfies(r -> {
assertThat(r.consumed()).isTrue();
});
verifier1.verify(Duration.ofSeconds(1));
verifier2.verify(Duration.ofSeconds(1));
}
}
@Nested
class Visual {
}
}