Evaluate asynchronously in a separate thread

Show calculating animation
This commit is contained in:
John Scipione 2013-06-13 00:07:37 -04:00
parent 159d1fb69a
commit 969fac14a7
2 changed files with 178 additions and 21 deletions

View File

@ -50,6 +50,10 @@
#define B_TRANSLATION_CONTEXT "CalcView"
static const int32 kMsgCalculating = 'calc';
static const int32 kMsgAnimateDots = 'dots';
static const int32 kMsgDoneEvaluating = 'done';
//const uint8 K_COLOR_OFFSET = 32;
const float kFontScaleY = 0.4f;
const float kFontScaleX = 0.4f;
@ -57,6 +61,8 @@ const float kExpressionFontScaleY = 0.6f;
const float kDisplayScaleY = 0.2f;
static const bigtime_t kFlashOnOffInterval = 100000;
static const bigtime_t kCalculatingInterval = 1000000;
static const bigtime_t kAnimationInterval = 333333;
static const float kMinimumWidthCompact = 130.0f;
static const float kMaximumWidthCompact = 400.0f;
@ -139,7 +145,11 @@ CalcView::CalcView(BRect frame, rgb_color rgbBaseColor, BMessage* settings)
fPopUpMenu(NULL),
fAutoNumlockItem(NULL),
fAudioFeedbackItem(NULL),
fOptions(new CalcOptions())
fOptions(new CalcOptions()),
fEvaluateThread(-1),
fEvaluateMessageRunner(NULL),
fEvaluateSemaphore(B_BAD_SEM_ID),
fEnabled(true)
{
// tell the app server not to erase our b/g
SetViewColor(B_TRANSPARENT_32_BIT);
@ -172,7 +182,11 @@ CalcView::CalcView(BMessage* archive)
fPopUpMenu(NULL),
fAutoNumlockItem(NULL),
fAudioFeedbackItem(NULL),
fOptions(new CalcOptions())
fOptions(new CalcOptions()),
fEvaluateThread(-1),
fEvaluateMessageRunner(NULL),
fEvaluateSemaphore(B_BAD_SEM_ID),
fEnabled(true)
{
// Do not restore the follow mode, in shelfs, we never follow.
SetResizingMode(B_FOLLOW_NONE);
@ -186,6 +200,8 @@ CalcView::~CalcView()
delete fKeypad;
delete fOptions;
free(fKeypadDescription);
delete fEvaluateMessageRunner;
delete_sem(fEvaluateSemaphore);
}
@ -303,6 +319,80 @@ CalcView::MessageReceived(BMessage* message)
break;
}
case kMsgAnimateDots:
{
int32 end = fExpressionTextView->TextLength();
int32 start = end - 3;
if (fEnabled || strcmp(fExpressionTextView->Text() + start, "...") != 0) {
// stop the message runner
delete fEvaluateMessageRunner;
fEvaluateMessageRunner = NULL;
break;
}
uint8 dot = 0;
if (message->FindUInt8("dot", &dot) == B_OK) {
rgb_color fontColor = fExpressionTextView->HighColor();
rgb_color backColor = fExpressionTextView->LowColor();
fExpressionTextView->SetStylable(true);
fExpressionTextView->SetFontAndColor(start, end, NULL, 0, &backColor);
fExpressionTextView->SetFontAndColor(start + dot - 1, start + dot, NULL,
0, &fontColor);
fExpressionTextView->SetStylable(false);
}
dot++;
if (dot == 4)
dot = 1;
delete fEvaluateMessageRunner;
BMessage animate(kMsgAnimateDots);
animate.AddUInt8("dot", dot);
fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
BMessenger(this), &animate, kAnimationInterval, 1);
break;
}
case kMsgCalculating:
{
// calculation has taken more than 3 seconds
if (fEnabled) {
// stop the message runner
delete fEvaluateMessageRunner;
fEvaluateMessageRunner = NULL;
break;
}
BString calculating;
calculating << B_TRANSLATE("Calculating") << "...";
fExpressionTextView->SetText(calculating.String());
delete fEvaluateMessageRunner;
BMessage animate(kMsgAnimateDots);
animate.AddUInt8("dot", 1U);
fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
BMessenger(this), &animate, kAnimationInterval, 1);
break;
}
case kMsgDoneEvaluating:
{
_SetEnabled(true);
rgb_color fontColor = fExpressionTextView->HighColor();
fExpressionTextView->SetFontAndColor(NULL, 0, &fontColor);
const char* result;
if (message->FindString("error", &result) == B_OK)
fExpressionTextView->SetText(result);
else if (message->FindString("value", &result) == B_OK)
fExpressionTextView->SetValue(result);
// stop the message runner
delete fEvaluateMessageRunner;
fEvaluateMessageRunner = NULL;
break;
}
default:
BView::MessageReceived(message);
break;
@ -591,7 +681,8 @@ CalcView::FrameResized(float width, float height)
? fHeight : fHeight * kDisplayScaleY;
BFont font(be_bold_font);
font.SetSize(sizeDisp * kExpressionFontScaleY);
fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL);
rgb_color fontColor = fExpressionTextView->HighColor();
fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fontColor);
expressionRect.OffsetTo(B_ORIGIN);
float inset = (expressionRect.Height() - fExpressionTextView->LineHeight(0)) / 2;
@ -736,31 +827,39 @@ CalcView::SaveSettings(BMessage* archive) const
void
CalcView::Evaluate()
{
BString expression = fExpressionTextView->Text();
if (expression.Length() == 0) {
if (fExpressionTextView->TextLength() == 0) {
beep();
return;
}
_AudioFeedback(false);
// evaluate expression
BString value;
try {
ExpressionParser parser;
parser.SetDegreeMode(fOptions->degree_mode);
value = parser.Evaluate(expression.String());
} catch (ParseException e) {
BString error(e.message.String());
error << " at " << (e.position + 1);
fExpressionTextView->SetText(error.String());
fEvaluateThread = spawn_thread(_EvaluateThread, "Evaluate Thread",
B_LOW_PRIORITY, this);
if (fEvaluateThread < B_OK) {
// failed to create evaluate thread, error out
fExpressionTextView->SetText(strerror(fEvaluateThread));
return;
}
// render new result to display
fExpressionTextView->SetValue(value.String());
_AudioFeedback(false);
_SetEnabled(false);
// Disable input while we evaluate
status_t threadStatus = resume_thread(fEvaluateThread);
if (threadStatus != B_OK) {
// evaluate thread failed to start, error out
fExpressionTextView->SetText(strerror(threadStatus));
_SetEnabled(true);
return;
}
if (fEvaluateMessageRunner == NULL) {
BMessage message(kMsgCalculating);
fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
BMessenger(this), &message, kCalculatingInterval, 1);
status_t runnerStatus = fEvaluateMessageRunner->InitCheck();
if (runnerStatus != B_OK)
printf("Evaluate Message Runner: %s\n", strerror(runnerStatus));
}
}
@ -895,6 +994,41 @@ CalcView::SetKeypadMode(uint8 mode)
// #pragma mark -
/*static*/ status_t
CalcView::_EvaluateThread(void* data)
{
CalcView* calcView = reinterpret_cast<CalcView*>(data);
if (calcView == NULL)
return B_BAD_TYPE;
BMessenger messenger(calcView);
if (!messenger.IsValid())
return B_BAD_VALUE;
BString result;
status_t status = acquire_sem(calcView->fEvaluateSemaphore);
if (status == B_OK) {
ExpressionParser parser;
parser.SetDegreeMode(calcView->fOptions->degree_mode);
BString expression(calcView->fExpressionTextView->Text());
try {
result = parser.Evaluate(expression.String());
} catch (ParseException e) {
result << e.message.String() << " at " << (e.position + 1);
status = B_ERROR;
}
release_sem(calcView->fEvaluateSemaphore);
} else
result = strerror(status);
BMessage message(kMsgDoneEvaluating);
message.AddString(status == B_OK ? "value" : "error", result.String());
messenger.SendMessage(&message);
return status;
}
void
CalcView::_Init(BMessage* settings)
{
@ -907,6 +1041,8 @@ CalcView::_Init(BMessage* settings)
// fetch the calc icon for compact view
_FetchAppIcon(fCalcIcon);
fEvaluateSemaphore = create_sem(1, "Evaluate Semaphore");
}
@ -1023,6 +1159,9 @@ CalcView::_ParseCalcDesc(const char* keypadDescription)
void
CalcView::_PressKey(int key)
{
if (!fEnabled)
return;
assert(key < (fRows * fColumns));
assert(key >= 0);
@ -1294,3 +1433,12 @@ CalcView::_IsEmbedded()
{
return Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0;
}
void
CalcView::_SetEnabled(bool enable)
{
fEnabled = enable;
fExpressionTextView->MakeSelectable(enable);
fExpressionTextView->MakeEditable(enable);
}

View File

@ -35,6 +35,7 @@ static const float kMaximumHeightBasic = 400.0f;
class BString;
class BMenuItem;
class BMessage;
class BMessageRunner;
class BPopUpMenu;
class CalcOptions;
class CalcOptionsWindow;
@ -97,6 +98,7 @@ class CalcView : public BView {
void SetKeypadMode(uint8 mode);
private:
static status_t _EvaluateThread(void* data);
void _Init(BMessage* settings);
status_t _LoadSettings(BMessage* archive);
void _ParseCalcDesc(const char* keypadDescription);
@ -119,6 +121,8 @@ class CalcView : public BView {
void _FetchAppIcon(BBitmap* into);
bool _IsEmbedded();
void _SetEnabled(bool enable);
// grid dimensions
int16 fColumns;
int16 fRows;
@ -161,6 +165,11 @@ class CalcView : public BView {
// calculator options.
CalcOptions* fOptions;
thread_id fEvaluateThread;
BMessageRunner* fEvaluateMessageRunner;
sem_id fEvaluateSemaphore;
bool fEnabled;
};
#endif // _CALC_VIEW_H