Editor/display changes
This commit is contained in:
parent
4b08fa133e
commit
4e42f6ad6b
@ -13,7 +13,10 @@ import static org.solovyev.android.calculator.CalculatorEventType.*;
|
||||
public class CalculatorDisplayImpl implements CalculatorDisplay {
|
||||
|
||||
@NotNull
|
||||
private CalculatorEventData lastCalculatorEventData;
|
||||
private volatile CalculatorEventData lastCalculatorEventData;
|
||||
|
||||
@NotNull
|
||||
private final Object lastCalculatorEventDataLock = new Object();
|
||||
|
||||
@Nullable
|
||||
private CalculatorDisplayView view;
|
||||
@ -88,8 +91,10 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
|
||||
@Override
|
||||
@NotNull
|
||||
public CalculatorEventData getLastEventData() {
|
||||
synchronized (lastCalculatorEventDataLock) {
|
||||
return lastCalculatorEventData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
|
||||
@ -97,10 +102,18 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
|
||||
@Nullable Object data) {
|
||||
if (calculatorEventType.isOfType(calculation_result, calculation_failed, calculation_cancelled, conversion_result, conversion_failed)) {
|
||||
|
||||
boolean processEvent = false;
|
||||
boolean sameSequence = false;
|
||||
|
||||
synchronized (lastCalculatorEventDataLock) {
|
||||
if (calculatorEventData.isAfter(lastCalculatorEventData)) {
|
||||
sameSequence = calculatorEventData.isSameSequence(lastCalculatorEventData);
|
||||
lastCalculatorEventData = calculatorEventData;
|
||||
processEvent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (processEvent) {
|
||||
switch (calculatorEventType) {
|
||||
case conversion_failed:
|
||||
processConversationFailed((CalculatorConversionEventData) calculatorEventData, (ConversionFailure) data);
|
||||
@ -118,7 +131,7 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
|
||||
processCalculationFailed((CalculatorEvaluationEventData)calculatorEventData, (CalculatorFailure) data);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@ public interface CalculatorEditor extends CalculatorEventListener {
|
||||
@NotNull
|
||||
CalculatorEditorViewState getViewState();
|
||||
|
||||
// updates state of view (view.setState())
|
||||
void updateViewState();
|
||||
|
||||
void setViewState(@NotNull CalculatorEditorViewState viewState);
|
||||
|
||||
/*
|
||||
|
@ -48,8 +48,17 @@ public class CalculatorEditorImpl implements CalculatorEditor {
|
||||
return lastViewState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateViewState() {
|
||||
setViewState(this.lastViewState, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewState(@NotNull CalculatorEditorViewState newViewState) {
|
||||
setViewState(newViewState, true);
|
||||
}
|
||||
|
||||
private void setViewState(CalculatorEditorViewState newViewState, boolean fireEvent) {
|
||||
synchronized (viewLock) {
|
||||
final CalculatorEditorViewState oldViewState = this.lastViewState;
|
||||
|
||||
@ -58,9 +67,11 @@ public class CalculatorEditorImpl implements CalculatorEditor {
|
||||
this.view.setState(newViewState);
|
||||
}
|
||||
|
||||
if (fireEvent) {
|
||||
calculator.fireCalculatorEvent(CalculatorEventType.editor_state_changed, new CalculatorEditorChangeEventDataImpl(oldViewState, newViewState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
|
||||
|
@ -40,7 +40,10 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
|
||||
|
||||
@NotNull
|
||||
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10);
|
||||
private final Executor calculationsExecutor = Executors.newFixedThreadPool(10);
|
||||
|
||||
@NotNull
|
||||
private final Executor eventExecutor = Executors.newFixedThreadPool(1);
|
||||
|
||||
public CalculatorImpl() {
|
||||
this.addCalculatorEventListener(this);
|
||||
@ -94,7 +97,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
|
||||
final CalculatorEventData eventDataId = nextEventData();
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
calculationsExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, null);
|
||||
@ -109,7 +112,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
public CalculatorEventData evaluate(@NotNull final JsclOperation operation, @NotNull final String expression, @NotNull Long sequenceId) {
|
||||
final CalculatorEventData eventDataId = nextEventData(sequenceId);
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
calculationsExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, null);
|
||||
@ -242,7 +245,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
final CalculatorDisplayViewState displayViewState = CalculatorLocatorImpl.getInstance().getDisplay().getViewState();
|
||||
final NumeralBase from = CalculatorLocatorImpl.getInstance().getEngine().getNumeralBase();
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
calculationsExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Long sequenceId = eventDataId.getSequenceId();
|
||||
@ -316,26 +319,31 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
||||
public void fireCalculatorEvent(@NotNull final CalculatorEventData calculatorEventData, @NotNull final CalculatorEventType calculatorEventType, @Nullable final Object data) {
|
||||
eventExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
|
||||
public void fireCalculatorEvents(@NotNull final List<CalculatorEvent> calculatorEvents) {
|
||||
eventExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
calculatorEventContainer.fireCalculatorEvents(calculatorEvents);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CalculatorEventData fireCalculatorEvent(@NotNull final CalculatorEventType calculatorEventType, @Nullable final Object data) {
|
||||
final CalculatorEventData eventData = nextEventData();
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fireCalculatorEvent(eventData, calculatorEventType, data);
|
||||
}
|
||||
});
|
||||
|
||||
return eventData;
|
||||
}
|
||||
@ -345,12 +353,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
|
||||
public CalculatorEventData fireCalculatorEvent(@NotNull final CalculatorEventType calculatorEventType, @Nullable final Object data, @NotNull Long sequenceId) {
|
||||
final CalculatorEventData eventData = nextEventData(sequenceId);
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fireCalculatorEvent(eventData, calculatorEventType, data);
|
||||
}
|
||||
});
|
||||
|
||||
return eventData;
|
||||
}
|
||||
|
@ -8,10 +8,12 @@ package org.solovyev.android.calculator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.calculator.view.TextHighlighter;
|
||||
import org.solovyev.android.view.AutoResizeTextView;
|
||||
@ -43,7 +45,12 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
|
||||
*/
|
||||
|
||||
@NotNull
|
||||
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
|
||||
private volatile CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
|
||||
|
||||
private volatile boolean viewStateChange = false;
|
||||
|
||||
@NotNull
|
||||
private final Object lock = new Object();
|
||||
|
||||
@NotNull
|
||||
private final Handler handler = new Handler();
|
||||
@ -58,14 +65,18 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context) {
|
||||
super(context);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -76,63 +87,103 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public boolean isValid() {
|
||||
synchronized (this) {
|
||||
return this.state.isValid();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(@NotNull final CalculatorDisplayViewState state) {
|
||||
handler.postDelayed(new Runnable() {
|
||||
final CharSequence text = prepareText(state.getStringResult(), state.isValid());
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (AndroidCalculatorDisplayView.this) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
viewStateChange = true;
|
||||
|
||||
AndroidCalculatorDisplayView.this.state = state;
|
||||
if ( state.isValid() ) {
|
||||
if (state.isValid()) {
|
||||
setTextColor(getResources().getColor(R.color.default_text_color));
|
||||
setText(state.getStringResult());
|
||||
redraw();
|
||||
setText(text);
|
||||
|
||||
adjustTextSize();
|
||||
|
||||
} else {
|
||||
// update text in order to get rid of HTML tags
|
||||
setText(getText().toString());
|
||||
setTextColor(getResources().getColor(R.color.display_error_text_color));
|
||||
|
||||
// error messages are never shown -> just greyed out text (error message will be shown on click)
|
||||
//setText(state.getErrorMessage());
|
||||
//redraw();
|
||||
}
|
||||
} finally {
|
||||
viewStateChange = false;
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CalculatorDisplayViewState getState() {
|
||||
synchronized (this) {
|
||||
synchronized (lock) {
|
||||
return this.state;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void redraw() {
|
||||
if (isValid()) {
|
||||
String text = getText().toString();
|
||||
@Nullable
|
||||
private static CharSequence prepareText(@Nullable String text, boolean valid) {
|
||||
CharSequence result;
|
||||
|
||||
Log.d(this.getClass().getName(), text);
|
||||
if (valid && text != null) {
|
||||
|
||||
//Log.d(this.getClass().getName(), text);
|
||||
|
||||
try {
|
||||
TextHighlighter.Result result = textHighlighter.process(text);
|
||||
text = result.toString();
|
||||
final TextHighlighter.Result processedText = textHighlighter.process(text);
|
||||
text = processedText.toString();
|
||||
result = Html.fromHtml(text);
|
||||
} catch (CalculatorParseException e) {
|
||||
Log.e(this.getClass().getName(), e.getMessage(), e);
|
||||
result = text;
|
||||
}
|
||||
} else {
|
||||
result = text;
|
||||
}
|
||||
|
||||
Log.d(this.getClass().getName(), text);
|
||||
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void adjustTextSize() {
|
||||
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
|
||||
setAddEllipsis(false);
|
||||
setMinTextSize(10);
|
||||
resizeText();
|
||||
}
|
||||
|
||||
|
||||
public void handleTextChange(Editable s) {
|
||||
synchronized (lock) {
|
||||
if (!viewStateChange) {
|
||||
// external text change => need to notify display
|
||||
// todo serso: implement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class TextWatcherImpl implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
handleTextChange(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,15 @@ import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.widget.EditText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.calculator.view.TextHighlighter;
|
||||
import org.solovyev.common.collections.CollectionsUtils;
|
||||
@ -31,26 +36,33 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
|
||||
private boolean highlightText = true;
|
||||
|
||||
@NotNull
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true);
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false);
|
||||
|
||||
@NotNull
|
||||
private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance();
|
||||
|
||||
private volatile boolean viewStateChange = false;
|
||||
|
||||
// NOTE: static because super constructor calls some overridden methods (like onSelectionChanged and current lock is not yet created)
|
||||
@NotNull
|
||||
private static final Object lock = new Object();
|
||||
|
||||
@NotNull
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
public AndroidCalculatorEditorView(Context context) {
|
||||
super(context);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
}
|
||||
|
||||
public AndroidCalculatorEditorView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
}
|
||||
|
||||
public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
this.addTextChangedListener(new TextWatcherImpl());
|
||||
}
|
||||
|
||||
|
||||
@ -59,12 +71,12 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
|
||||
// NOTE: code below can be used carefully and should not be copied without special intention
|
||||
// The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc
|
||||
|
||||
if ( Build.VERSION.SDK_INT >= 11 ) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
// fix for missing cursor in android 3 and higher
|
||||
try {
|
||||
// IDEA: return false always except if method was called from TextView.isCursorVisible() method
|
||||
for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) {
|
||||
if ( "isCursorVisible".equals(stackTraceElement.getMethodName()) ) {
|
||||
if ("isCursorVisible".equals(stackTraceElement.getMethodName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -85,38 +97,30 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
|
||||
menu.removeItem(android.R.id.selectAll);
|
||||
}
|
||||
|
||||
// todo serso: fix redraw
|
||||
// Now problem is that calculator editor cursor position might be different than position of cursor in view (as some extra spaces can be inserted fur to number formatting)
|
||||
/*private synchronized void redraw() {
|
||||
String text = getText().toString();
|
||||
|
||||
int selectionStart = getSelectionStart();
|
||||
int selectionEnd = getSelectionEnd();
|
||||
@Nullable
|
||||
private CharSequence prepareText(@NotNull String text, boolean highlightText) {
|
||||
CharSequence result;
|
||||
|
||||
if (highlightText) {
|
||||
|
||||
Log.d(this.getClass().getName(), text);
|
||||
|
||||
try {
|
||||
final TextHighlighter.Result result = textHighlighter.process(text);
|
||||
selectionStart += result.getOffset();
|
||||
selectionEnd += result.getOffset();
|
||||
text = result.toString();
|
||||
final TextHighlighter.Result processesText = textHighlighter.process(text);
|
||||
|
||||
assert processesText.getOffset() == 0;
|
||||
|
||||
result = Html.fromHtml(processesText.toString());
|
||||
} catch (CalculatorParseException e) {
|
||||
// set raw text
|
||||
result = text;
|
||||
|
||||
Log.e(this.getClass().getName(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
Log.d(this.getClass().getName(), text);
|
||||
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
|
||||
} else {
|
||||
super.setText(text, BufferType.EDITABLE);
|
||||
result = text;
|
||||
}
|
||||
|
||||
Log.d(this.getClass().getName(), getText().toString());
|
||||
|
||||
int length = getText().length();
|
||||
setSelection(Math.max(Math.min(length, selectionStart), 0), Math.max(Math.min(length, selectionEnd), 0));
|
||||
}*/
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isHighlightText() {
|
||||
return highlightText;
|
||||
@ -124,7 +128,7 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
|
||||
|
||||
public void setHighlightText(boolean highlightText) {
|
||||
this.highlightText = highlightText;
|
||||
//redraw();
|
||||
CalculatorLocatorImpl.getInstance().getEditor().updateViewState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,32 +144,61 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
|
||||
|
||||
@Override
|
||||
public void setState(@NotNull final CalculatorEditorViewState viewState) {
|
||||
handler.postDelayed(new Runnable() {
|
||||
|
||||
final CharSequence text = prepareText(viewState.getText(), highlightText);
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this;
|
||||
synchronized (editorView) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
editorView.viewStateChange = true;
|
||||
editorView.viewState = viewState;
|
||||
editorView.setText(viewState.getText());
|
||||
editorView.setText(text, BufferType.EDITABLE);
|
||||
editorView.setSelection(viewState.getSelection());
|
||||
//redraw();
|
||||
} finally {
|
||||
editorView.viewStateChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelectionChanged(int selStart, int selEnd) {
|
||||
synchronized (this) {
|
||||
synchronized (lock) {
|
||||
if (!viewStateChange) {
|
||||
// external text change => need to notify editor
|
||||
super.onSelectionChanged(selStart, selEnd);
|
||||
CalculatorLocatorImpl.getInstance().getEditor().setSelection(selStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTextChange(Editable s) {
|
||||
synchronized (lock) {
|
||||
if (!viewStateChange) {
|
||||
// external text change => need to notify editor
|
||||
CalculatorLocatorImpl.getInstance().getEditor().setText(String.valueOf(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class TextWatcherImpl implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
handleTextChange(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user