Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added double-click support #842

Merged
merged 2 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
import java.util.stream.Stream;
Expand All @@ -43,7 +47,10 @@ class FlowPanel extends JPanel {

private static final long serialVersionUID = 1L;

private final transient Filter filter;
/**
* The {@link Filter} that is maniupated by this widget
*/
final transient Filter filter;

/**
* Map from list content to flow
Expand Down Expand Up @@ -81,6 +88,10 @@ class FlowPanel extends JPanel {
}
return d;
};

/**
* Invoked when the {@link Filter} changes
*/
private transient Runnable updateListener = () -> {
// no-op
};
Expand Down Expand Up @@ -125,6 +136,13 @@ class FlowPanel extends JPanel {
include.setEnabled( !enabledFlows.isSelectionEmpty() || !disabledFlows.isSelectionEmpty() );
exclude.setEnabled( !enabledFlows.isSelectionEmpty() );
} );
setDoubleClickAction( enabledFlows, ( set, index ) -> {
if( set.isEmpty() ) {
listedFlows.values().forEach( f -> set.add( f.index ) );
}
set.remove( index );
} );

disabledFlows.addListSelectionListener( lse -> {
if( !clearing.get() ) {
clearing.set( true );
Expand All @@ -134,6 +152,7 @@ class FlowPanel extends JPanel {
include.setEnabled( !enabledFlows.isSelectionEmpty() || !disabledFlows.isSelectionEmpty() );
exclude.setEnabled( !enabledFlows.isSelectionEmpty() );
} );
setDoubleClickAction( disabledFlows, Set::add );

include.setToolTipText( "Enable selected flows" );
include.addActionListener( ac -> {
Expand Down Expand Up @@ -226,6 +245,29 @@ class FlowPanel extends JPanel {

}

private void setDoubleClickAction( JList<String> list,
BiConsumer<Set<Integer>, Integer> action ) {
list.addMouseListener( new MouseAdapter() {
@Override
public void mouseClicked( MouseEvent e ) {
if( e.getClickCount() == 2 ) {
Optional.of( e.getPoint() )
.map( list::locationToIndex )
.map( list.getModel()::getElementAt )
.map( listedFlows::get )
.map( ifl -> ifl.index )
.ifPresent( idx -> {
Set<Integer> indices = filter.indices();
action.accept( indices, idx );
filter.indices( indices );
updateListener.run();
refresh();
} );
}
}
} );
}

/**
* @param l Called whenever the flow selection is changed
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
Expand All @@ -39,7 +42,10 @@ class TagPanel extends JPanel {

private static final long serialVersionUID = 1L;

private final transient Filter filter;
/**
* The {@link Filter} being manipulated by this widget
*/
final transient Filter filter;

private final JList<String> availableTags = new JList<>();
private final JList<String> includedTags = new JList<>();
Expand All @@ -60,7 +66,10 @@ class TagPanel extends JPanel {
*/
transient Predicate<String> tagLimit = t -> true;

private transient Runnable updateListener = () -> {
/**
* Invoked when the {@link Filter} changes
*/
transient Runnable updateListener = () -> {
// no-op
};

Expand Down Expand Up @@ -119,6 +128,8 @@ class TagPanel extends JPanel {
avin.setEnabled( !availableTags.isSelectionEmpty() || !includedTags.isSelectionEmpty() );
avex.setEnabled( !availableTags.isSelectionEmpty() || !excludedTags.isSelectionEmpty() );
} );
setDoubleClickAction( availableTags, Set::add, null );

includedTags.addListSelectionListener( lse -> {
if( !clearing.get() ) {
clearing.set( true );
Expand All @@ -129,6 +140,8 @@ class TagPanel extends JPanel {
avin.setEnabled( !availableTags.isSelectionEmpty() || !includedTags.isSelectionEmpty() );
inex.setEnabled( !includedTags.isSelectionEmpty() || !excludedTags.isSelectionEmpty() );
} );
setDoubleClickAction( includedTags, Set::remove, null );

excludedTags.addListSelectionListener( lse -> {
if( !clearing.get() ) {
clearing.set( true );
Expand All @@ -139,6 +152,7 @@ class TagPanel extends JPanel {
avex.setEnabled( !availableTags.isSelectionEmpty() || !excludedTags.isSelectionEmpty() );
inex.setEnabled( !includedTags.isSelectionEmpty() || !excludedTags.isSelectionEmpty() );
} );
setDoubleClickAction( excludedTags, null, Set::remove );

avin.setToolTipText( SWAP_SELECTED_TAGS );
avin.addActionListener( ac -> {
Expand Down Expand Up @@ -246,6 +260,35 @@ class TagPanel extends JPanel {
refresh();
}

private void setDoubleClickAction( JList<String> list,
BiConsumer<Set<String>, String> includeAction,
BiConsumer<Set<String>, String> excludeAction ) {
list.addMouseListener( new MouseAdapter() {
@Override
public void mouseClicked( MouseEvent e ) {
if( e.getClickCount() == 2 ) {
String item = list.getModel().getElementAt(
list.locationToIndex( e.getPoint() ) );

if( includeAction != null ) {
Set<String> s = filter.includedTags();
includeAction.accept( s, item );
filter.includedTags( s );
}

if( excludeAction != null ) {
Set<String> s = filter.excludedTags();
excludeAction.accept( s, item );
filter.excludedTags( s );
}

updateListener.run();
refresh();
}
}
} );
}

/**
* @param l executed whenever the filter is updated
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ public FilterGuiHarness selectTags( TagList in, String... tags ) {
return this;
}

/**
* Double-clicks on a list item
*
* @param in The list that the item resides in
* @param tag The item to double-click
* @return <code>this</code>
*/
public FilterGuiHarness doubleClick( TagList in, String tag ) {
interactions.add( ( f, m ) -> {
f.list( in.widgetName ).item( tag ).doubleClick();
} );
return this;
}

/**
* Clicks one of the tags swap buttons
*
Expand Down Expand Up @@ -188,6 +202,20 @@ public FilterGuiHarness selectFlows( FlowList list, String... flows ) {
return this;
}

/**
* Double-clicks on a list item
*
* @param in The list that the item resides in
* @param flow The item to double-click
* @return <code>this</code>
*/
public FilterGuiHarness doubleClick( FlowList in, String flow ) {
interactions.add( ( f, m ) -> {
f.list( in.widgetName ).item( FilterGuiHarness.toRendered( flow ) ).doubleClick();
} );
return this;
}

private static final Pattern TO_RENDERED = Pattern.compile(
"(.*) \\| (.*)" );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;

import com.mastercard.test.flow.assrt.filter.Filter;
import com.mastercard.test.flow.assrt.filter.gui.FilterGuiHarness.FlowList;
import com.mastercard.test.flow.assrt.filter.gui.FilterGuiHarness.TagList;
import com.mastercard.test.flow.assrt.filter.mock.Mdl;
Expand Down Expand Up @@ -234,4 +236,20 @@ void combined() {
.expectFilterResults(
"fourth [bar, foo]" );
}

/**
* Throws up a gui instance for you to fiddle with for manual testing
*/
@Test()
@EnabledIfSystemProperty(named = "manual", matches = "true")
void manual() {
Mdl mdl = new Mdl().withFlows(
"first [foo, bar]",
"second [bar, baz]",
"third [baz, oof]",
"fourth [foo, bar]" );
Filter filter = new Filter( mdl );
FilterGui gui = new FilterGui( filter );
gui.blockForInput();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,54 @@ void rebuild() {
.on( mdl );
}

/**
* Shows how flows can be moved between the enabled and disabled set by
* double-clicking
*/
@Test
void doubleClick() {

Mdl mdl = new Mdl().withFlows(
"bcd []",
"fga []",
"jkl []",
"mne []" );

new FilterGuiHarness()
.buildFlows()
.doubleClick( FlowList.ENABLED, "jkl | " )
.expect( "before",
"┌─ avail… ─┐┌┈┈┈┐┌─ inclu… ─┐┌─ disable… ─┐┌┈┈┈┐┌─ enabled_flo… ─┐╔═════╗",
"│ │┊ _ ┊│ ││ jkl | │┊ _ ┊│ bcd | │║ ║",
"│ │└┈┈┈┘└──────────┘│ │┊ ┊│ fga | │║ ║",
"│ │ ┌┈┈┈┈┈┈┈┈┈┈┐│ │└┈┈┈┘│ mne | │║ ║",
"│ │ ┊ _ ┊│ │┌┈┈┈┐│ │║ ║",
"│ │ └┈┈┈┈┈┈┈┈┈┈┘│ │┊ ┊│ │║ Run ║",
"│ │┌┈┈┈┐┌─ exclu… ─┐│ │┊ _ ┊│ │║ ║",
"│ │┊ _ ┊│ ││ │┊ ┊│ │║ ║",
"└──────────┘└┈┈┈┘└──────────┘└────────────┘└┈┈┈┘└────────────────┘║ ║",
"┌┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐╔═══════════════════════════════════╗║ ║",
"┊ Reset ┊║ Reset ║║ ║",
"└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘╚═══════════════════════════════════╝╚═════╝" )
.doubleClick( FlowList.DISABLED, "jkl | " )
.doubleClick( FlowList.ENABLED, "bcd | " )
.expect( "after",
"┌─ avail… ─┐┌┈┈┈┐┌─ inclu… ─┐┌─ disable… ─┐┌┈┈┈┐┌─ enabled_flo… ─┐╔═════╗",
"│ │┊ _ ┊│ ││ bcd | │┊ _ ┊│ fga | │║ ║",
"│ │└┈┈┈┘└──────────┘│ │┊ ┊│ jkl | │║ ║",
"│ │ ┌┈┈┈┈┈┈┈┈┈┈┐│ │└┈┈┈┘│ mne | │║ ║",
"│ │ ┊ _ ┊│ │┌┈┈┈┐│ │║ ║",
"│ │ └┈┈┈┈┈┈┈┈┈┈┘│ │┊ ┊│ │║ Run ║",
"│ │┌┈┈┈┐┌─ exclu… ─┐│ │┊ _ ┊│ │║ ║",
"│ │┊ _ ┊│ ││ │┊ ┊│ │║ ║",
"└──────────┘└┈┈┈┘└──────────┘└────────────┘└┈┈┈┘└────────────────┘║ ║",
"┌┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐╔═══════════════════════════════════╗║ ║",
"┊ Reset ┊║ Reset ║║ ║",
"└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘╚═══════════════════════════════════╝╚═════╝" )
.close()
.on( mdl );
}

/**
* Shows how the filter input shuffles the tag and flow lists
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,54 @@ void selection() {
.on( mdl );
}

/**
* Shows that tags can be moved between sets by double-clicking
*/
@Test
void doubleClick() {
Mdl mdl = new Mdl().withFlows(
"first [foo, bar]",
"second [bar, baz]",
"third [baz, oof]" );

new FilterGuiHarness()
.selectTags( TagList.AVAILABLE, "bar" )
.moveTo( TagList.INCLUDED )
.selectTags( TagList.AVAILABLE, "oof" )
.moveTo( TagList.EXCLUDED )
.expect( "before",
"┌─ avail… ─┐┌┈┈┈┐┌─ inclu… ─┐╔═══════╗┌┈┈┈┈┈┐",
"│ baz │┊ _ ┊│ bar │║ ║┊ ┊",
"│ foo │└┈┈┈┘└──────────┘║ ║┊ ┊",
"│ │ ┌┈┈┈┈┈┈┈┈┈┈┐║ ║┊ ┊",
"│ │ ┊ _ ┊║ ║┊ ┊",
"│ │ └┈┈┈┈┈┈┈┈┈┈┘║ Build ║┊ Run ┊",
"│ │┌┈┈┈┐┌─ exclu… ─┐║ ║┊ ┊",
"│ │┊ _ ┊│ oof │║ ║┊ ┊",
"└──────────┘└┈┈┈┘└──────────┘║ ║┊ ┊",
"╔═══════════════════════════╗║ ║┊ ┊",
"║ Reset ║║ ║┊ ┊",
"╚═══════════════════════════╝╚═══════╝└┈┈┈┈┈┘" )
.doubleClick( TagList.AVAILABLE, "baz" )
.doubleClick( TagList.INCLUDED, "bar" )
.doubleClick( TagList.EXCLUDED, "oof" )
.expect( "after",
"┌─ avail… ─┐┌┈┈┈┐┌─ inclu… ─┐╔═══════╗┌┈┈┈┈┈┐",
"│ bar │┊ _ ┊│ baz │║ ║┊ ┊",
"│ foo │└┈┈┈┘└──────────┘║ ║┊ ┊",
"│ oof │ ┌┈┈┈┈┈┈┈┈┈┈┐║ ║┊ ┊",
"│ │ ┊ _ ┊║ ║┊ ┊",
"│ │ └┈┈┈┈┈┈┈┈┈┈┘║ Build ║┊ Run ┊",
"│ │┌┈┈┈┐┌─ exclu… ─┐║ ║┊ ┊",
"│ │┊ _ ┊│ │║ ║┊ ┊",
"└──────────┘└┈┈┈┘└──────────┘║ ║┊ ┊",
"╔═══════════════════════════╗║ ║┊ ┊",
"║ Reset ║║ ║┊ ┊",
"╚═══════════════════════════╝╚═══════╝└┈┈┈┈┈┘" )
.close()
.on( mdl );
}

/**
* Exercises the tag reset button
*/
Expand Down