mirror of
https://github.com/TrianguloY/LightningLauncher.git
synced 2024-12-26 09:58:20 +01:00
Merge scriptEditor branch
This commit is contained in:
commit
782406cb58
9 changed files with 726 additions and 46 deletions
|
@ -24,6 +24,7 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -51,7 +52,6 @@ import net.pierrox.lightning_launcher.data.Utils;
|
|||
import net.pierrox.lightning_launcher.engine.LightningEngine;
|
||||
import net.pierrox.lightning_launcher.script.Script;
|
||||
import net.pierrox.lightning_launcher.script.ScriptManager;
|
||||
import net.pierrox.lightning_launcher.script.api.Array;
|
||||
import net.pierrox.lightning_launcher.script.api.Box;
|
||||
import net.pierrox.lightning_launcher.script.api.Container;
|
||||
import net.pierrox.lightning_launcher.script.api.Desktop;
|
||||
|
@ -64,16 +64,18 @@ import net.pierrox.lightning_launcher.script.api.ImageNinePatch;
|
|||
import net.pierrox.lightning_launcher.script.api.ImageScript;
|
||||
import net.pierrox.lightning_launcher.script.api.ImageSvg;
|
||||
import net.pierrox.lightning_launcher.script.api.Item;
|
||||
import net.pierrox.lightning_launcher.script.api.LL;
|
||||
import net.pierrox.lightning_launcher.script.api.Lightning;
|
||||
import net.pierrox.lightning_launcher.script.api.PageIndicator;
|
||||
import net.pierrox.lightning_launcher.script.api.Panel;
|
||||
import net.pierrox.lightning_launcher.script.api.PropertyEditor;
|
||||
import net.pierrox.lightning_launcher.script.api.PropertySet;
|
||||
import net.pierrox.lightning_launcher.script.api.RectL;
|
||||
import net.pierrox.lightning_launcher.script.api.Shortcut;
|
||||
import net.pierrox.lightning_launcher.script.api.StopPoint;
|
||||
import net.pierrox.lightning_launcher.util.FileAndDirectoryPickerDialog;
|
||||
import net.pierrox.lightning_launcher.util.FileProvider;
|
||||
import net.pierrox.lightning_launcher.util.Indentation;
|
||||
import net.pierrox.lightning_launcher.util.Search;
|
||||
import net.pierrox.lightning_launcher_extreme.BuildConfig;
|
||||
import net.pierrox.lightning_launcher_extreme.R;
|
||||
|
||||
|
@ -99,6 +101,7 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
private static final String PREF_LAST_SCRIPT_ID = "se_lsi";
|
||||
private static final String PREF_LAST_SCRIPT_LINE = "se_lsl";
|
||||
private static final String PREF_WORDWRAP = "se_w";
|
||||
private static final String PREF_AUTOINDENT = "se_ind";
|
||||
private static final String PREF_FONT_SIZE = "se_fs";
|
||||
private static final String PREF_DIRECTORY = "se_d";
|
||||
private static final String PREF_SUB_DIRS = "se_sd";
|
||||
|
@ -122,6 +125,8 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
private CheckBox mMenuCustom;
|
||||
private ArrayAdapter<Script> mScriptAdapter;
|
||||
private List<Script> mAllScripts = new ArrayList<>();
|
||||
private Indentation mIndentation;
|
||||
private Search mSearch;
|
||||
|
||||
private boolean mShowSubDirs;
|
||||
|
||||
|
@ -302,25 +307,27 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
mLeftPaneAnimOut = AnimationUtils.makeOutAnimation(this, false);
|
||||
|
||||
// mCompletionsListView = (ListView) findViewById(R.id.completions);
|
||||
mCompletionsViewGroup = (ViewGroup) findViewById(R.id.completions);
|
||||
mCompletionsViewGroup = findViewById(R.id.completions);
|
||||
initializeShortcuts((ViewGroup) findViewById(R.id.shortcuts));
|
||||
|
||||
Button btn;
|
||||
|
||||
btn = (Button)findViewById(R.id.sc_import);
|
||||
btn = findViewById(R.id.sc_import);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_import);
|
||||
btn = (Button)findViewById(R.id.sc_new);
|
||||
btn = findViewById(R.id.sc_new);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_new);
|
||||
btn = (Button)findViewById(R.id.sc_delete);
|
||||
btn = findViewById(R.id.sc_delete);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_delete);
|
||||
btn = (Button)findViewById(R.id.sc_edit);
|
||||
btn = findViewById(R.id.sc_edit);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_edit);
|
||||
btn = (Button)findViewById(R.id.sc_help);
|
||||
btn = findViewById(R.id.sc_help);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_help);
|
||||
btn = (Button)findViewById(R.id.sc_send);
|
||||
btn = findViewById(R.id.sc_send);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setText(R.string.sc_send);
|
||||
|
||||
|
@ -329,20 +336,20 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
|
||||
((TextView)findViewById(R.id.sc_path)).setText(R.string.sc_path);
|
||||
((TextView)findViewById(R.id.sc_name)).setText(R.string.sc_name);
|
||||
mScriptSpinner = (Spinner) findViewById(R.id.sc_spinner);
|
||||
mScriptSpinner = findViewById(R.id.sc_spinner);
|
||||
mScriptSpinner.setLongClickable(true);
|
||||
mScriptSpinner.setOnItemSelectedListener(this);
|
||||
mScriptSpinner.setAdapter(mScriptAdapter);
|
||||
updateScriptsSpinner();
|
||||
|
||||
btn = (Button)findViewById(R.id.sc_edit_name);
|
||||
btn = findViewById(R.id.sc_edit_name);
|
||||
btn.setOnClickListener(this);
|
||||
btn.setTypeface(LLApp.get().getIconsTypeface());
|
||||
|
||||
if(sAutoCompleteTokens == null) {
|
||||
buildAutoCompleteTokens();
|
||||
}
|
||||
mScriptText = (AdvancedEditText) findViewById(R.id.sc_text);
|
||||
mScriptText = findViewById(R.id.sc_text);
|
||||
mScriptText.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL));
|
||||
mScriptText.addTextChangedListener(mScriptTextWatcher);
|
||||
mScriptText.setListener(new AdvancedEditText.OnAdvancedEditTextEvent() {
|
||||
|
@ -377,19 +384,21 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
mScriptText.setTextSize(size);
|
||||
}
|
||||
});
|
||||
mIndentation = new Indentation();
|
||||
mSearch = new Search(this, mScriptText);
|
||||
|
||||
((TextView)findViewById(R.id.sc_ma)).setText(R.string.sc_ma);
|
||||
((TextView)findViewById(R.id.sc_a)).setText(R.string.sc_a);
|
||||
((TextView)findViewById(R.id.sc_h)).setText(R.string.sc_h);
|
||||
mMenuLightning = (CheckBox) findViewById(R.id.sc_ml);
|
||||
mMenuLightning = findViewById(R.id.sc_ml);
|
||||
mMenuLightning.setText(R.string.sc_ml);
|
||||
mMenuItem = (CheckBox) findViewById(R.id.sc_mi);
|
||||
mMenuItem = findViewById(R.id.sc_mi);
|
||||
mMenuItem.setText(R.string.sc_mi);
|
||||
mMenuCustom = (CheckBox) findViewById(R.id.sc_mc);
|
||||
mMenuCustom = findViewById(R.id.sc_mc);
|
||||
mMenuCustom.setText(R.string.sc_mc);
|
||||
|
||||
mShowSubDirs = mSharedPrefs.getBoolean(PREF_SUB_DIRS, true);
|
||||
CheckBox showSubDirs = (CheckBox) findViewById(R.id.sc_sd);
|
||||
CheckBox showSubDirs = findViewById(R.id.sc_sd);
|
||||
showSubDirs.setText(R.string.sc_all);
|
||||
showSubDirs.setChecked(mShowSubDirs);
|
||||
showSubDirs.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
|
@ -404,7 +413,7 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
}
|
||||
});
|
||||
|
||||
mSelectDirectoryButton = (Button) findViewById(R.id.sc_d);
|
||||
mSelectDirectoryButton = findViewById(R.id.sc_d);
|
||||
mSelectDirectoryButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -427,13 +436,20 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
|
||||
boolean wordwrap = mSharedPrefs.getBoolean(PREF_WORDWRAP, true);
|
||||
mScriptText.setWordWrap(wordwrap);
|
||||
boolean autoindent = mSharedPrefs.getBoolean(PREF_AUTOINDENT, true);
|
||||
if(autoindent) mIndentation.register(mScriptText);
|
||||
float text_size = mSharedPrefs.getFloat(PREF_FONT_SIZE, mScriptText.getTextSize() / mScaledDensity);
|
||||
mScriptText.setTextSize(text_size);
|
||||
((TextView)findViewById(R.id.sc_o)).setText(R.string.sc_o);
|
||||
CheckBox wordwrap_checkbox = (CheckBox) findViewById(R.id.sc_w);
|
||||
CheckBox wordwrap_checkbox = findViewById(R.id.sc_w);
|
||||
wordwrap_checkbox.setText(R.string.sc_w);
|
||||
wordwrap_checkbox.setChecked(wordwrap);
|
||||
wordwrap_checkbox.setOnCheckedChangeListener(this);
|
||||
CheckBox autoindent_checkbox = findViewById(R.id.sc_ind);
|
||||
autoindent_checkbox.setText(R.string.sc_ind);
|
||||
autoindent_checkbox.setChecked(autoindent);
|
||||
autoindent_checkbox.setOnCheckedChangeListener(this);
|
||||
|
||||
|
||||
TextPosition position = null;
|
||||
int sel_start = 0, sel_end = 0;
|
||||
|
@ -572,7 +588,7 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
.putFloat(PREF_FONT_SIZE, mScriptText.getTextSize()/mScaledDensity)
|
||||
.putString(PREF_DIRECTORY, mCurrentDirectory.getAbsolutePath())
|
||||
.putBoolean(PREF_SUB_DIRS, mShowSubDirs)
|
||||
.commit();
|
||||
.apply();
|
||||
|
||||
if(mScript.id >= 0) {
|
||||
setLastScriptId();
|
||||
|
@ -620,12 +636,12 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
|
||||
View content = getLayoutInflater().inflate(R.layout.edit_script_dialog, null);
|
||||
|
||||
final EditText nameEditText = (EditText) content.findViewById(R.id.sc_name);
|
||||
final EditText nameEditText = content.findViewById(R.id.sc_name);
|
||||
nameEditText.setText(mScript.name);
|
||||
nameEditText.setSelection(mScript.name.length());
|
||||
|
||||
final EditText pathEditText = (EditText) content.findViewById(R.id.sc_path);
|
||||
final Button pickPathButton = (Button) content.findViewById(R.id.sc_pick_path);
|
||||
final EditText pathEditText = content.findViewById(R.id.sc_path);
|
||||
final Button pickPathButton = content.findViewById(R.id.sc_pick_path);
|
||||
pickPathButton.setTypeface(LLApp.get().getIconsTypeface());
|
||||
pickPathButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -840,7 +856,16 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
switch (buttonView.getId()) {
|
||||
case R.id.sc_w:
|
||||
mScriptText.setWordWrap(isChecked);
|
||||
mSharedPrefs.edit().putBoolean(PREF_WORDWRAP, isChecked).commit();
|
||||
mSharedPrefs.edit().putBoolean(PREF_WORDWRAP, isChecked).apply();
|
||||
break;
|
||||
case R.id.sc_ind:
|
||||
// toggled autoindentation
|
||||
if(isChecked){
|
||||
mIndentation.register(mScriptText);
|
||||
} else {
|
||||
mIndentation.unregister(mScriptText);
|
||||
}
|
||||
mSharedPrefs.edit().putBoolean(PREF_AUTOINDENT, isChecked).apply();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -918,8 +943,8 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
}
|
||||
|
||||
private void setLastScriptId() {
|
||||
mSharedPrefs.edit().putInt(PREF_LAST_SCRIPT_ID, mScript.id).commit();
|
||||
mSharedPrefs.edit().putInt(PREF_LAST_SCRIPT_LINE, mScriptText.getSelectionLine()).commit();
|
||||
mSharedPrefs.edit().putInt(PREF_LAST_SCRIPT_ID, mScript.id).apply();
|
||||
mSharedPrefs.edit().putInt(PREF_LAST_SCRIPT_LINE, mScriptText.getSelectionLine()).apply();
|
||||
}
|
||||
|
||||
private void showLeftPane() {
|
||||
|
@ -954,7 +979,6 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
private void buildAutoCompleteTokens() {
|
||||
sAutoCompleteTokens = new ArrayList<>();
|
||||
Class<?>[] classes = {
|
||||
Array.class,
|
||||
Box.class,
|
||||
net.pierrox.lightning_launcher.script.api.Binding.class,
|
||||
Container.class,
|
||||
|
@ -968,7 +992,6 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
ImageScript.class,
|
||||
ImageSvg.class,
|
||||
Item.class,
|
||||
//LL.class,
|
||||
Lightning.class,
|
||||
Panel.class,
|
||||
StopPoint.class,
|
||||
|
@ -979,7 +1002,7 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
PropertySet.class,
|
||||
RectL.class,
|
||||
net.pierrox.lightning_launcher.script.api.Script.class,
|
||||
Shortcut.class,
|
||||
net.pierrox.lightning_launcher.script.api.Shortcut.class,
|
||||
net.pierrox.lightning_launcher.script.api.Lightning.class,
|
||||
net.pierrox.lightning_launcher.script.api.Menu.class,
|
||||
ComponentName.class,
|
||||
|
@ -1246,4 +1269,187 @@ public class ScriptEditor extends ResourceWrapperActivity implements View.OnClic
|
|||
b.setOnLongClickListener(mCompletionButtonLongClickListener);
|
||||
mCompletionsViewGroup.addView(b);
|
||||
}
|
||||
|
||||
// -------------- shortcuts --------------------
|
||||
|
||||
/**
|
||||
* Represents a button in the bottom of the editor.
|
||||
*/
|
||||
private interface Shortcut {
|
||||
/**
|
||||
* Label to display
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* @return true if label is an icon (using LL typeface). False if normal text
|
||||
*/
|
||||
boolean isLabelIcon();
|
||||
|
||||
/**
|
||||
* Runned when the button is pressed
|
||||
*/
|
||||
void apply(AdvancedEditText editText);
|
||||
}
|
||||
|
||||
/**
|
||||
* This button will send a key to the editText.
|
||||
*/
|
||||
private static class ShortcutKey implements Shortcut {
|
||||
private int key;
|
||||
private String label;
|
||||
|
||||
ShortcutKey(int key, String iconLabel) {
|
||||
this.key = key;
|
||||
this.label = iconLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLabelIcon() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(AdvancedEditText editText) {
|
||||
editText.dispatchKeyEvent(new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
|
||||
key, 0));
|
||||
editText.dispatchKeyEvent(new KeyEvent(0, 0, KeyEvent.ACTION_UP,
|
||||
key, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This button will insert a text before and after the cursor.
|
||||
*/
|
||||
private static class ShortcutText implements Shortcut {
|
||||
private String preText;
|
||||
private String postText;
|
||||
|
||||
ShortcutText(String preText, String postText) {
|
||||
this.preText = preText;
|
||||
this.postText = postText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return preText + "·" + postText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLabelIcon() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(AdvancedEditText editText) {
|
||||
int start = editText.getSelectionStart();
|
||||
int end = editText.getSelectionEnd();
|
||||
editText.getEditableText().replace(start, end, preText+postText);
|
||||
editText.setSelection(start + preText.length());
|
||||
}
|
||||
}
|
||||
|
||||
enum SA {
|
||||
DEC_TAB,
|
||||
INC_TAB,
|
||||
SEARCH,
|
||||
SEARCHN,
|
||||
}
|
||||
/**
|
||||
* This button will trigger an action
|
||||
*/
|
||||
private class ShortcutAction implements Shortcut {
|
||||
private SA action;
|
||||
|
||||
ShortcutAction(SA action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
switch (action) {
|
||||
case DEC_TAB:
|
||||
return "A"; // left arrow with bar
|
||||
case INC_TAB:
|
||||
return "D"; // right arrow with bar
|
||||
case SEARCH:
|
||||
return "I"; // magnifier glass
|
||||
case SEARCHN:
|
||||
return "IC"; // magnifier glass and '>'
|
||||
}
|
||||
return "n"; // android icon
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLabelIcon() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(AdvancedEditText editText) {
|
||||
switch (action) {
|
||||
case DEC_TAB:
|
||||
Pair<Integer, Integer> selectionD =
|
||||
Indentation.modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), false, editText.getEditableText());
|
||||
editText.setSelection(selectionD.first, selectionD.second);
|
||||
break;
|
||||
case INC_TAB:
|
||||
Pair<Integer, Integer> selectionI =
|
||||
Indentation.modifyIndent(editText.getSelectionStart(), editText.getSelectionEnd(), true, editText.getEditableText());
|
||||
editText.setSelection(selectionI.first, selectionI.second);
|
||||
break;
|
||||
case SEARCH:
|
||||
mSearch.showDialog();
|
||||
break;
|
||||
case SEARCHN:
|
||||
mSearch.searchNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// list of shortcuts to display
|
||||
private Shortcut[] mShortcuts = new Shortcut[]{
|
||||
new ShortcutKey(KeyEvent.KEYCODE_DPAD_LEFT, "|"),
|
||||
new ShortcutKey(KeyEvent.KEYCODE_DPAD_UP, "~"),
|
||||
new ShortcutKey(KeyEvent.KEYCODE_DPAD_DOWN, "{"),
|
||||
new ShortcutKey(KeyEvent.KEYCODE_DPAD_RIGHT, "}"),
|
||||
new ShortcutAction(SA.DEC_TAB),
|
||||
new ShortcutAction(SA.INC_TAB),
|
||||
new ShortcutText("(", ")"),
|
||||
new ShortcutText("[", "]"),
|
||||
new ShortcutText("{", "}"),
|
||||
new ShortcutText("var ", ""),
|
||||
new ShortcutAction(SA.SEARCH),
|
||||
new ShortcutAction(SA.SEARCHN),
|
||||
};
|
||||
|
||||
private View.OnClickListener mShortcutButtonClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Button btn = (Button) v;
|
||||
Shortcut shortcut = (Shortcut) btn.getTag();
|
||||
shortcut.apply(mScriptText);
|
||||
//mInputMethodManager.restartInput(mScriptText);
|
||||
}
|
||||
};
|
||||
|
||||
private void initializeShortcuts(ViewGroup view) {
|
||||
Typeface typeface = LLApp.get().getIconsTypeface();
|
||||
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
for (Shortcut shortcut : mShortcuts) {
|
||||
Button b = (Button) inflater.inflate(R.layout.sc_btn, null);
|
||||
b.setTag(shortcut);
|
||||
b.setText(shortcut.getLabel());
|
||||
if(shortcut.isLabelIcon())
|
||||
b.setTypeface(typeface);
|
||||
b.setOnClickListener(mShortcutButtonClickListener);
|
||||
view.addView(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
package net.pierrox.lightning_launcher.util;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Pair;
|
||||
import android.widget.EditText;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class to manage the Indentation of an EditText.
|
||||
* When registered as the TextWatcher of an edittext it will provide autoindentation on writing
|
||||
* Other functions are available to manage indentation on Editables
|
||||
*/
|
||||
public class Indentation {
|
||||
private static final int INDENT_SIZE = 2;
|
||||
|
||||
private Map<EditText, IndentationTextWatcher> mRegistered = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Registers the AutoIndentation on the provided editText (unless it was already registered)
|
||||
*/
|
||||
public void register(EditText editText){
|
||||
if(mRegistered.containsKey(editText))
|
||||
return;
|
||||
|
||||
IndentationTextWatcher itw = new IndentationTextWatcher(editText);
|
||||
editText.addTextChangedListener(itw);
|
||||
mRegistered.put(editText, itw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the AutoIndentation on the provided editText (if it was registered before)
|
||||
*/
|
||||
public void unregister(EditText editText){
|
||||
if(!mRegistered.containsKey(editText))
|
||||
return;
|
||||
|
||||
editText.removeTextChangedListener(mRegistered.get(editText));
|
||||
mRegistered.remove(editText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an ending bracket is inserted. Decreases the indentation.
|
||||
* @param posBracket where the endbracket was
|
||||
* @param editable where to unindent
|
||||
*/
|
||||
private void onEndBracket(int posBracket, Editable editable){
|
||||
|
||||
// check if beginning of line
|
||||
if( posBracket == getLineIndent(posBracket, editable).second ){
|
||||
// decrease indent
|
||||
decreaseIndent( posBracket, editable );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a newline is inserted. Indents it with the same indentation as the previous line (if any)
|
||||
* @param posEnter position of the newline char
|
||||
* @param editable where to indent
|
||||
*/
|
||||
private void onNewLine(int posEnter, Editable editable){
|
||||
|
||||
int n = getLineIndent(posEnter, editable).first;
|
||||
StringBuilder indent = new StringBuilder();
|
||||
for(int i=0;i<n;++i){
|
||||
indent.append(" ");
|
||||
}
|
||||
|
||||
// do if previous line ends in open bracket
|
||||
if(posEnter > 0 && editable.charAt(posEnter - 1) == '{'){
|
||||
|
||||
// add newline if also following close bracket
|
||||
if(posEnter < editable.length() - 1 && editable.charAt(posEnter + 1) == '}'){
|
||||
editable.insert(posEnter + 2, "\n" + indent.toString() + "}");
|
||||
// this avoids moving the cursor
|
||||
editable.replace(posEnter + 1, posEnter + 2, "");
|
||||
}
|
||||
|
||||
// add indent size
|
||||
for(int i=0;i<INDENT_SIZE;++i){
|
||||
indent.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// write indent
|
||||
editable.insert(posEnter + 1, indent.toString());
|
||||
}
|
||||
|
||||
// ------------ functions -----------------
|
||||
|
||||
/**
|
||||
* Returns the size of the indent in the current line (spaces at the left) and the position of the first non-space char
|
||||
* @param currentpos pos of current line (any char)
|
||||
* @param editable where to search
|
||||
* @return length of indent (number of spaces) and position of first non-space char (can be end of file)
|
||||
*/
|
||||
private static Pair<Integer, Integer> getLineIndent(int currentpos, Editable editable){
|
||||
// goto beginning of line
|
||||
if(currentpos != 0) {
|
||||
do{
|
||||
currentpos--;
|
||||
}while (currentpos >= 0 && editable.charAt(currentpos) != '\n');
|
||||
currentpos++;
|
||||
}
|
||||
|
||||
// find indent size
|
||||
int n = 0;
|
||||
boolean cont = true;
|
||||
while(cont && currentpos < editable.length()){
|
||||
switch (editable.charAt(currentpos)){
|
||||
case ' ':
|
||||
n++;
|
||||
currentpos++;
|
||||
break;
|
||||
case '\t':
|
||||
n+=INDENT_SIZE;
|
||||
currentpos++;
|
||||
break;
|
||||
//case '\n':
|
||||
default:
|
||||
cont = false;
|
||||
}
|
||||
}
|
||||
return new Pair<>(n, currentpos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the indentation of all the lines selected
|
||||
* @param posLeft start of selection
|
||||
* @param posRight end of selection
|
||||
* @param increase if true increase indent, decrease otherwise
|
||||
* @param editable where to apply the indentation
|
||||
* @return the new selection (may have changed due to the indentation changes)
|
||||
*/
|
||||
public static Pair<Integer, Integer> modifyIndent(int posLeft, int posRight, boolean increase, Editable editable){
|
||||
String span = "modifyIntent";
|
||||
editable.setSpan(span, posLeft, posRight, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
|
||||
Deque<Integer> positions = new ArrayDeque<>();
|
||||
while(posLeft <= posRight && posLeft <= editable.length()){
|
||||
// mark position to indent
|
||||
positions.push(posLeft);
|
||||
|
||||
// find next line (next newline char)
|
||||
posLeft++;
|
||||
while(posLeft <= posRight && posLeft <= editable.length() && editable.charAt(posLeft - 1) != '\n')
|
||||
posLeft++;
|
||||
}
|
||||
|
||||
for (Integer position : positions) {
|
||||
// indent the lines in reverse order
|
||||
if(increase)
|
||||
increaseIndent(position, editable);
|
||||
else
|
||||
decreaseIndent(position, editable);
|
||||
}
|
||||
|
||||
//restore span
|
||||
return new Pair<>(editable.getSpanStart(span), editable.getSpanEnd(span));
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the indentation of a single line
|
||||
* @param posCursor position of a character in the line that will be indented
|
||||
* @param editable where to apply the indentation
|
||||
*/
|
||||
private static void increaseIndent(int posCursor, Editable editable){
|
||||
|
||||
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
|
||||
int beg = n_beg.second;
|
||||
|
||||
// increase indent adding spaces
|
||||
for(int i=0; i< INDENT_SIZE; i++) editable.insert(beg, " ");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the indentation of a single line
|
||||
* @param posCursor position of a character in the line that will be indented
|
||||
* @param editable where to apply the indentation
|
||||
*/
|
||||
private static void decreaseIndent(int posCursor, Editable editable){
|
||||
|
||||
Pair<Integer, Integer> n_beg = getLineIndent(posCursor, editable);
|
||||
int n = n_beg.first;
|
||||
int beg = n_beg.second;
|
||||
|
||||
if ( n >= INDENT_SIZE ){
|
||||
// enough intent to remove, remove the first tab, or all the spaces if no tabs found
|
||||
int p = 1;
|
||||
while (p <= INDENT_SIZE) {
|
||||
if (editable.charAt(beg - p) == '\t') {
|
||||
//tab found, remove
|
||||
editable.delete(beg - p, beg - p + 1);
|
||||
return;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
// no tabs found, only spaces, remove them
|
||||
editable.delete(beg - INDENT_SIZE, beg);
|
||||
}else{
|
||||
// not enough indent (so no tabs), just remove all the previous spaces
|
||||
editable.delete(beg - n, beg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------ textwatcher ----------------
|
||||
|
||||
private class IndentationTextWatcher implements TextWatcher {
|
||||
|
||||
IndentationTextWatcher(EditText mEditText) {
|
||||
this.mEditText = mEditText;
|
||||
}
|
||||
|
||||
private String mSpanNewline = "mSpanNewline";
|
||||
private String mSpanEndBracket = "mSpanEndBracket";
|
||||
private boolean mEditing = false;
|
||||
private EditText mEditText;
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if(mEditing) return;
|
||||
|
||||
if(count == 1 && s.charAt(start) == '\n'){
|
||||
// newline inserted
|
||||
mEditText.getEditableText().setSpan(mSpanNewline,start,start,0);
|
||||
}
|
||||
if(count == 1 && s.charAt(start) == '}'){
|
||||
// end bracket inserted
|
||||
mEditText.getEditableText().setSpan(mSpanEndBracket, start, start, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mEditing = true;
|
||||
int spanPos;
|
||||
|
||||
// check newline
|
||||
spanPos = editable.getSpanStart(mSpanNewline);
|
||||
editable.removeSpan(mSpanNewline);
|
||||
if (spanPos != -1 && editable.charAt(spanPos) == '\n')
|
||||
onNewLine(spanPos, editable);
|
||||
|
||||
// check endbracket
|
||||
spanPos = editable.getSpanStart(mSpanEndBracket);
|
||||
editable.removeSpan(mSpanEndBracket);
|
||||
if (spanPos != -1 && editable.charAt(spanPos) == '}')
|
||||
onEndBracket(spanPos, editable);
|
||||
|
||||
mEditing = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package net.pierrox.lightning_launcher.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
|
||||
import net.pierrox.lightning_launcher_extreme.R;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import fr.xgouchet.texteditor.ui.AdvancedEditText;
|
||||
|
||||
/**
|
||||
* Class that Manages a searchable dialog (currently for the script editor).
|
||||
*/
|
||||
public class Search {
|
||||
private Activity mCntx;
|
||||
private AdvancedEditText mEditText;
|
||||
|
||||
private AlertDialog mDialog;
|
||||
private EditText mEdTxt;
|
||||
private CheckBox mChkBackwards;
|
||||
private CheckBox mChkCase;
|
||||
private CheckBox mChkRegexp;
|
||||
|
||||
public Search(Activity cntx, AdvancedEditText editText) {
|
||||
mCntx = cntx;
|
||||
mEditText = editText;
|
||||
|
||||
// The dialog is saved and created on first use
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog to search.
|
||||
*/
|
||||
public void showDialog(){
|
||||
if(mDialog == null)
|
||||
createDialog();
|
||||
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search as defined in the dialog.
|
||||
* If the search fails the dialog is shown.
|
||||
*/
|
||||
public void searchNext() {
|
||||
if(mDialog == null)
|
||||
createDialog();
|
||||
|
||||
String searchText = mEdTxt.getText().toString();
|
||||
|
||||
if(searchText.isEmpty()) {
|
||||
// no text to search
|
||||
showDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
String text = mEditText.getText().toString();
|
||||
|
||||
int flags = 0;
|
||||
if(!mChkCase.isChecked()) flags |= Pattern.CASE_INSENSITIVE;
|
||||
if(!mChkRegexp.isChecked()) flags |= Pattern.LITERAL;
|
||||
|
||||
Pattern pattern = Pattern.compile(searchText, flags);
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if(!mChkBackwards.isChecked()) {
|
||||
// search fordwards
|
||||
int from = mEditText.getSelectionStart();
|
||||
if (from != mEditText.getSelectionEnd()) from++; // avoids returning the current selection
|
||||
if( matcher.find(from) || matcher.find(0)){
|
||||
// found one just after the selection or from the beginning
|
||||
start = matcher.start();
|
||||
end = matcher.end();
|
||||
}
|
||||
}else{
|
||||
// search backwards
|
||||
|
||||
int until = mEditText.getSelectionEnd();
|
||||
while( matcher.find() && matcher.end() < until){
|
||||
// found match before cursor, save
|
||||
start = matcher.start();
|
||||
end = matcher.end();
|
||||
}
|
||||
|
||||
if(start == -1){
|
||||
// not found, continue to find last one
|
||||
while( matcher.find() ){
|
||||
// found match, save
|
||||
start = matcher.start();
|
||||
end = matcher.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( start != -1) {
|
||||
// found, set selection
|
||||
mEditText.setSelection(start, end);
|
||||
}else{
|
||||
// nothing found
|
||||
showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private: creates the dialog
|
||||
*/
|
||||
private void createDialog(){
|
||||
|
||||
View views = mCntx.getLayoutInflater().inflate(R.layout.dialog_search, null);
|
||||
|
||||
mEdTxt = views.findViewById(R.id.srch_text);
|
||||
mChkBackwards = views.findViewById(R.id.srch_back);
|
||||
mChkCase = views.findViewById(R.id.srch_case);
|
||||
mChkRegexp = views.findViewById(R.id.srch_regexp);
|
||||
|
||||
mDialog = new AlertDialog.Builder(mCntx)
|
||||
.setTitle(R.string.srch_ttl)
|
||||
.setView(views)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(android.R.string.search_go, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
// After this code a 'dialog.dismiss()' will be called, so the dialog won't be shown again if necessary.
|
||||
// By posting a runnable for the next iteration, the code runs after the dismiss call and the dialog will be shown again if required.
|
||||
// Other possible solutions require to override the button listener after the call to show (but the dialog won't 'fade out/in')
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
}
|
||||
}
|
33
app/llx/app/src/main/res/layout/dialog_search.xml
Normal file
33
app/llx/app/src/main/res/layout/dialog_search.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/srch_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="text" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/srch_back"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/srch_back" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/srch_case"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/srch_case" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/srch_regexp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/srch_regexp" />
|
||||
</LinearLayout>
|
|
@ -13,31 +13,49 @@
|
|||
android:id="@+id/sc_h"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/sc_h"
|
||||
android:textStyle="italic"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<fr.xgouchet.texteditor.ui.AdvancedEditText
|
||||
android:id="@+id/sc_text"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/sc_text" />
|
||||
android:layout_weight="1">
|
||||
|
||||
<fr.xgouchet.texteditor.ui.AdvancedEditText
|
||||
android:id="@+id/sc_text"
|
||||
style="@style/sc_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/completions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</HorizontalScrollView>
|
||||
</FrameLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shortcuts"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/completions"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/left_pane"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -162,6 +180,13 @@
|
|||
android:checked="true"
|
||||
android:text="@string/sc_w"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/sc_ind"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/sc_ind" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sc_a"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
Binary file not shown.
|
@ -1315,4 +1315,9 @@
|
|||
<string name="permission_description">view, create, edit, delete and run scripts in Lightning Launcher</string>
|
||||
|
||||
<string name="sc_disable_toast">Script disabled. Open it in the editor to enable again</string>
|
||||
<string name="srch_ttl">Search text</string>
|
||||
<string name="srch_back">Backwards</string>
|
||||
<string name="srch_regexp">Regexp</string>
|
||||
<string name="srch_case">Match case</string>
|
||||
<string name="sc_ind">Autoindentation</string>
|
||||
</resources>
|
||||
|
|
BIN
graphics/font/Material-Design-Iconic-Font-2.2.ttf
Normal file
BIN
graphics/font/Material-Design-Iconic-Font-2.2.ttf
Normal file
Binary file not shown.
BIN
graphics/font/map.png
Normal file
BIN
graphics/font/map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
Loading…
Reference in a new issue