Add graphical toolbox, instead of text menu.
This commit is contained in:
parent
b0fc1df080
commit
486c342f86
@ -524,7 +524,7 @@ public class ProgramakerCustomBlockPart implements Part, ProgramakerSignalListen
|
||||
// Listen to signal
|
||||
ProgramakerApi api = _partGrid.getApi();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -70,22 +70,22 @@ public class RoundButton implements Part {
|
||||
|
||||
@Override
|
||||
public int get_left() {
|
||||
return _xCenter - _outerRadius / 2;
|
||||
return _xCenter - _outerRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get_right() {
|
||||
return _xCenter + _outerRadius / 2;
|
||||
return _xCenter + _outerRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get_top() {
|
||||
return _yCenter - _outerRadius / 2;
|
||||
return _yCenter - _outerRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get_bottom() {
|
||||
return _yCenter + _outerRadius / 2;
|
||||
return _yCenter + _outerRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
@ -2,9 +2,16 @@ package com.codigoparallevar.minicards.toolbox;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
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.PartInstantiator;
|
||||
@ -14,12 +21,13 @@ import com.codigoparallevar.minicards.parts.logic.Toggle;
|
||||
import com.codigoparallevar.minicards.parts.samples.ColorBox;
|
||||
import com.codigoparallevar.minicards.parts.strings.ConvertToString;
|
||||
import com.codigoparallevar.minicards.types.Part;
|
||||
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||
import com.codigoparallevar.minicards.utils.Box;
|
||||
import com.programaker.api.data.ProgramakerBridgeInfo;
|
||||
import com.programaker.api.data.ProgramakerCustomBlock;
|
||||
import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -42,6 +50,7 @@ public class PartsHolder {
|
||||
add(new PartCategory("Testing", BuiltInParts));
|
||||
}};
|
||||
private Map<String, ProgramakerBridgeInfo> bridgeInfoMap;
|
||||
private final static String LogTag = "PartsHolder";
|
||||
|
||||
public PartsHolder(Context context) {
|
||||
this.context = context;
|
||||
@ -49,61 +58,113 @@ public class PartsHolder {
|
||||
|
||||
public void openAddPartModal(final CanvasView canvasView) {
|
||||
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")
|
||||
.setItems(categoryNames, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichCategory) {
|
||||
if ((whichCategory >= 0) && (whichCategory < categoryNames.length)){
|
||||
Map<String, PartInstantiator> parts = getPartTypes(categoryOptions.get(categoryNames[whichCategory]));
|
||||
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);
|
||||
// HACK : Nasty trick to make the accordion be able to close a dialog that still doesn't exist
|
||||
Box<Dialog> openedDialog = new Box<>();
|
||||
Consumer<Void> onComplete = _void -> {
|
||||
if (openedDialog.get() == null) {
|
||||
Log.e(LogTag, "Expected dialog to be opened already");
|
||||
}
|
||||
});
|
||||
else {
|
||||
openedDialog.get().cancel();
|
||||
}
|
||||
};
|
||||
|
||||
Dialog dialog2 = builder.create();
|
||||
dialog2.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
View categoryAccordion = generateCategoryAccordion(canvasView, onComplete);
|
||||
|
||||
builder.setTitle("Choose part")
|
||||
.setView(categoryAccordion);
|
||||
|
||||
Dialog dialog = builder.create();
|
||||
openedDialog.set(dialog);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void runInstantiator(PartInstantiator instantiator, CanvasView canvasView) {
|
||||
Part part = instantiator.build(canvasView);
|
||||
private View generateCategoryAccordion(final CanvasView canvasView, Consumer<Void> signalComplete) {
|
||||
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);
|
||||
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() {
|
||||
Map<String, PartCategory> partTypes = new LinkedHashMap<>(Categories.size());
|
||||
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;
|
||||
view.addView(layout);
|
||||
return view;
|
||||
}
|
||||
|
||||
public void addCustomBlocks(List<ProgramakerBridgeInfo> bridgeInfo, List<ProgramakerBridgeCustomBlockResult> customBlocks) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
17
app/src/main/res/layout/toolbox_accordion.xml
Normal file
17
app/src/main/res/layout/toolbox_accordion.xml
Normal 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>
|
Loading…
Reference in New Issue
Block a user