Add graphical toolbox, instead of text menu.

This commit is contained in:
Sergio Martínez Portela 2020-06-04 19:44:48 +02:00
parent b0fc1df080
commit 486c342f86
6 changed files with 241 additions and 48 deletions

View File

@ -524,7 +524,7 @@ public class ProgramakerCustomBlockPart implements Part, ProgramakerSignalListen
// Listen to signal // Listen to signal
ProgramakerApi api = _partGrid.getApi(); ProgramakerApi api = _partGrid.getApi();
if (api == null) { if (api == null) {
Log.e(LogTag, "Cannot listen to API (API not found)"); Log.i(LogTag, "Cannot listen to API. API not found, probably on a showcase.");
return; return;
} }

View File

@ -70,22 +70,22 @@ public class RoundButton implements Part {
@Override @Override
public int get_left() { public int get_left() {
return _xCenter - _outerRadius / 2; return _xCenter - _outerRadius;
} }
@Override @Override
public int get_right() { public int get_right() {
return _xCenter + _outerRadius / 2; return _xCenter + _outerRadius;
} }
@Override @Override
public int get_top() { public int get_top() {
return _yCenter - _outerRadius / 2; return _yCenter - _outerRadius;
} }
@Override @Override
public int get_bottom() { public int get_bottom() {
return _yCenter + _outerRadius / 2; return _yCenter + _outerRadius;
} }
@Override @Override

View File

@ -0,0 +1,95 @@
package com.codigoparallevar.minicards.toolbox;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
import com.codigoparallevar.minicards.PartInstantiator;
import com.codigoparallevar.minicards.ScrolledCanvas;
import com.codigoparallevar.minicards.SignalListenerManager;
import com.codigoparallevar.minicards.types.Part;
import com.codigoparallevar.minicards.types.PartGrid;
import com.codigoparallevar.minicards.types.Selectable;
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
import com.codigoparallevar.minicards.types.functional.Tuple2;
import com.programaker.api.ProgramakerApi;
class PartShowcaseView extends View implements PartGrid {
private final Context ctx;
private final PartInstantiator instantiator;
private final Part part;
private final int partWidth;
private Tuple2<Integer, Integer> center = new Tuple2<>(0, 0);
public PartShowcaseView(Context ctx, PartInstantiator instantiator) {
super(ctx);
this.ctx = ctx;
this.instantiator = instantiator;
this.part = instantiator.build(this);
this.part.pause();
int height = this.part.get_bottom() - this.part.get_top();
partWidth = this.part.get_right() - this.part.get_left();
this.setMinimumHeight(height * 2);
this.setMinimumWidth(partWidth);
}
@Override
public void onDraw(Canvas canvas) {
int centerx = - Math.max(canvas.getWidth(), partWidth) / 2;
int centery = - canvas.getHeight() / 2;
this.center = new Tuple2<>(centerx, centery);
ScrolledCanvas scrolledCanvas = new ScrolledCanvas(canvas, getCenteredOn());
this.part.draw(scrolledCanvas, true);
}
// Most interactive functionalities are not implemented, as it's not needed for the showcase
@Override
public Selectable getPartOn(int x, int y) {
return null;
}
@Override
public ProgramakerApi getApi() {
return null;
}
@Override
public SignalListenerManager getListenerManager() {
return null;
}
@Override
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
return null;
}
@Override
public BooleanInputConnector getBooleanInputConnectorOn(int x, int y) {
return null;
}
@Override
public AnyInputConnector getAnyInputConnectorOn(int x, int y) {
return null;
}
@Override
public StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd) {
return null;
}
@Override
public Tuple2<Integer, Integer> getCenteredOn() {
return center;
}
@Override
public void update() {
}
}

View File

@ -2,9 +2,16 @@ package com.codigoparallevar.minicards.toolbox;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.graphics.Color;
import androidx.appcompat.app.AlertDialog; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.codigoparallevar.minicards.CanvasView; import com.codigoparallevar.minicards.CanvasView;
import com.codigoparallevar.minicards.PartInstantiator; import com.codigoparallevar.minicards.PartInstantiator;
@ -14,12 +21,13 @@ import com.codigoparallevar.minicards.parts.logic.Toggle;
import com.codigoparallevar.minicards.parts.samples.ColorBox; import com.codigoparallevar.minicards.parts.samples.ColorBox;
import com.codigoparallevar.minicards.parts.strings.ConvertToString; import com.codigoparallevar.minicards.parts.strings.ConvertToString;
import com.codigoparallevar.minicards.types.Part; import com.codigoparallevar.minicards.types.Part;
import com.codigoparallevar.minicards.types.functional.Consumer;
import com.codigoparallevar.minicards.types.functional.Tuple2; import com.codigoparallevar.minicards.types.functional.Tuple2;
import com.codigoparallevar.minicards.utils.Box;
import com.programaker.api.data.ProgramakerBridgeInfo; import com.programaker.api.data.ProgramakerBridgeInfo;
import com.programaker.api.data.ProgramakerCustomBlock; import com.programaker.api.data.ProgramakerCustomBlock;
import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult; import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -42,6 +50,7 @@ public class PartsHolder {
add(new PartCategory("Testing", BuiltInParts)); add(new PartCategory("Testing", BuiltInParts));
}}; }};
private Map<String, ProgramakerBridgeInfo> bridgeInfoMap; private Map<String, ProgramakerBridgeInfo> bridgeInfoMap;
private final static String LogTag = "PartsHolder";
public PartsHolder(Context context) { public PartsHolder(Context context) {
this.context = context; this.context = context;
@ -49,61 +58,113 @@ public class PartsHolder {
public void openAddPartModal(final CanvasView canvasView) { public void openAddPartModal(final CanvasView canvasView) {
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
Map<String, PartCategory> categoryOptions = getPartCategories();
String[] categoryNames = categoryOptions.keySet().toArray(new String[0]);
builder.setTitle("Choose part category") // HACK : Nasty trick to make the accordion be able to close a dialog that still doesn't exist
.setItems(categoryNames, new DialogInterface.OnClickListener() { Box<Dialog> openedDialog = new Box<>();
public void onClick(DialogInterface dialog, int whichCategory) { Consumer<Void> onComplete = _void -> {
if ((whichCategory >= 0) && (whichCategory < categoryNames.length)){ if (openedDialog.get() == null) {
Map<String, PartInstantiator> parts = getPartTypes(categoryOptions.get(categoryNames[whichCategory])); Log.e(LogTag, "Expected dialog to be opened already");
String[] partNames = parts.keySet().toArray(new String[0]);
builder.setTitle("Choose part type")
.setItems(partNames, (dialog1, whichPart) -> {
if ((whichPart >= 0) && (whichPart < partNames.length)) {
String partName = partNames[whichPart];
Log.d("Minicards partsHolder",
"Spawning " + partName);
PartInstantiator instantiator = parts.get(partName);
PartsHolder.this.runInstantiator(instantiator, canvasView);
} }
}); else {
openedDialog.get().cancel();
}
};
Dialog dialog2 = builder.create(); View categoryAccordion = generateCategoryAccordion(canvasView, onComplete);
dialog2.show();
} builder.setTitle("Choose part")
} .setView(categoryAccordion);
});
Dialog dialog = builder.create(); Dialog dialog = builder.create();
openedDialog.set(dialog);
dialog.show(); dialog.show();
} }
private void runInstantiator(PartInstantiator instantiator, CanvasView canvasView) { private View generateCategoryAccordion(final CanvasView canvasView, Consumer<Void> signalComplete) {
Part part = instantiator.build(canvasView); ScrollView view = new ScrollView(context);
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setDividerPadding(10);
layout.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
layout.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright));
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
for (int i = 0; i < Categories.size(); i++) {
PartCategory category = Categories.get(i);
LinearLayout categoryRow = new LinearLayout(context);
LinearLayout showcases = new LinearLayout(context);
TextView categoryName = new TextView(context); //Creating Button
// Add category name
categoryName.setId(i); //Setting Id for using in future
categoryName.setText(category.getName()); //Setting text
categoryName.setTextSize(15); //Text Size
categoryName.setPadding(50, 50, 50, 50); //padding
categoryName.setLayoutParams(params); //Setting Layout Params
categoryName.setTextColor(Color.parseColor("#000000")); //Text Color
categoryName.setBackgroundColor(Color.parseColor("#ffffff"));
categoryName.setGravity(Gravity.CENTER);
categoryName.setClickable(true);
categoryName.setOnClickListener(v -> {
if (showcases.getVisibility() == View.GONE) {
showcases.setVisibility(View.VISIBLE);
}
else {
showcases.setVisibility(View.GONE);
}
});
showcases.setVisibility(View.GONE);
showcases.setBackgroundColor(Color.parseColor("#BBBBBB"));
showcases.setOrientation(LinearLayout.VERTICAL);
showcases.setDividerPadding(10);
showcases.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
showcases.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright));
}
// Add showcases
int partHeights = 0;
for (Tuple2<String, PartInstantiator> entry : category.getParts()) {
PartShowcaseView showcase = new PartShowcaseView(context, entry.item2);
showcase.setClickable(true);
showcase.setOnClickListener(v -> {
Part part = entry.item2.build(canvasView);
canvasView.addPart(part); canvasView.addPart(part);
try {
signalComplete.apply(null);
} catch (Exception e) {
e.printStackTrace();
}
});
partHeights += showcase.getHeight();
showcases.addView(showcase);
}
showcases.setMinimumHeight(partHeights);
// Tie everything
categoryRow.setOrientation(LinearLayout.VERTICAL);
categoryRow.addView(categoryName);
showcases.setOrientation(LinearLayout.VERTICAL);
categoryRow.addView(showcases);
layout.addView(categoryRow);
} }
private static Map<String,PartCategory> getPartCategories() { view.addView(layout);
Map<String, PartCategory> partTypes = new LinkedHashMap<>(Categories.size()); return view;
for (int i = 0; i < Categories.size(); i++){
partTypes.put(Categories.get(i).getName(), Categories.get(i));
}
return partTypes;
}
private static Map<String, PartInstantiator> getPartTypes(PartCategory categoryOption) {
HashMap<String, PartInstantiator> partMap = new LinkedHashMap<>();
for (Tuple2<String, PartInstantiator> part : categoryOption.getParts()) {
partMap.put(part.item1, part.item2);
}
return partMap;
} }
public void addCustomBlocks(List<ProgramakerBridgeInfo> bridgeInfo, List<ProgramakerBridgeCustomBlockResult> customBlocks) { public void addCustomBlocks(List<ProgramakerBridgeInfo> bridgeInfo, List<ProgramakerBridgeCustomBlockResult> customBlocks) {

View File

@ -0,0 +1,20 @@
package com.codigoparallevar.minicards.utils;
public class Box<T> {
private T val = null;
public Box() {
}
public Box(T val) {
this.val = val;
}
public T get() {
return val;
}
public void set(T val) {
this.val = val;
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/toolbox_accordion_list"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>