Merge scriptEditor branch

This commit is contained in:
Pierrot 2019-04-29 10:39:09 +02:00
commit 782406cb58
9 changed files with 726 additions and 46 deletions

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

View 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>

View file

@ -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"

View file

@ -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>

Binary file not shown.

BIN
graphics/font/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB