Widget::~Widget() { // DEBUG: Not sure if still need to do this now that widget is removed after event queue processing, but maybe still needed to rid of persistent pointers // For all connected pointers // DEBUG: Disabled because it caused intermittent crashing of FolderListingWidget... // DEBUG: Re-enabled because fixed root cause of crashing, it was this widget being deleted more than once because I wasn't resetting m_Child back to nullptr after deleting while (!ModifyGestureRecognizer().GetConnected().empty()) { // Modify the pointer mapping of said pointer, and removed the widget which is being removed ModifyGestureRecognizer().GetConnected().begin().operator *()->ModifyPointerMapping().RemoveMapping(ModifyGestureRecognizer()); } }
void ConceptStringBoxWidget::UpdateGestureRecognizer() { //ModifyGestureRecognizer().m_RecognizeTap = true; // HACK: Recognize only taps when unselected; but this needs to be automated ModifyGestureRecognizer().m_RecognizeTap = !HasTypingFocus(); }
void MultitouchTestBoxWidget::ProcessTap(const InputEvent & InputEvent, Vector2n Position) { ++m_Color; if (m_Color >= 6) m_Color = 0; g_InputManager->RequestTypingPointer(ModifyGestureRecognizer()); }
ButtonWidget::ButtonWidget(Vector2n Position, Vector2n Dimensions, std::function<void()> Action) : Widget(Position, Dimensions), m_Action(Action) { assert(nullptr != m_Action); ModifyGestureRecognizer().m_RecognizeTap = true; }
void Widget::ProcessCanvasUpdated() { /*if (nullptr != m_GestureRecognizerTEST) { m_GestureRecognizerTEST->ProcessCanvasUpdated(); }*/ ModifyGestureRecognizer().ProcessCanvasUpdated(); }
template <typename T> void ListWidget<T>::ProcessManipulationStarted(const PointerState & PointerState) { if (!HasTypingFocus()) { auto ParentLocalPosition = GlobalToParent(Vector2n(PointerState.GetAxisState(0).GetPosition(), PointerState.GetAxisState(1).GetPosition())); ModifyGestureRecognizer().m_ManipulationOffset = GetPosition() - ParentLocalPosition; } }
MultitouchTestBoxWidget::MultitouchTestBoxWidget(Vector2n Position) : Widget(Position, Vector2n(200, 200), { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }), // m_GestureRecognizerTEST(), m_Color(rand() % 6) // HACK { // m_GestureRecognizerTEST.m_Widget = this; // m_GestureRecognizerTEST.m_OnTap = &MultitouchTestBoxWidget::ProcessTap; ModifyGestureRecognizer().m_RecognizeTap = true; }
// TODO: I've made this into a multi-line edit box, so change class name from Field (i.e. 1 line) to Box TextFieldWidget::TextFieldWidget(Vector2n Position, TypingModule & TypingModule) : Widget(Position, Vector2n(904, (3 + 2/*f.body_lines.size()*/) * lineHeight)), m_OnChange(), m_Content(), m_ContentLines(), m_MaxLineLength(0), m_CaretPosition(0), m_TargetCaretColumnX(0), m_SelectionPosition(0), m_TypingModule(TypingModule), m_BackgroundColor(static_cast<uint8>(255), 255, 255) { ModifyGestureRecognizer().m_RecognizeTap = true; ModifyGestureRecognizer().m_RecognizeDoubleTap = true; ModifyGestureRecognizer().m_RecognizeManipulationTranslate = true; UpdateContentLines(); // This is here at least for resize }
template <typename T> ListWidget<T>::ListWidget(Vector2n Position, std::vector<T> & List, TypingModule & TypingModule) : CompositeWidget(Position, { /*std::shared_ptr<Widget>(new ButtonWidget(Vector2n(-1, -18), Vector2n(lineHeight, lineHeight), [&]() { // TEST: This is specific stuff for quick testing if (!m_List.empty()) { m_List.pop_back(); } } ))*/ }), m_TapAction(), m_List(List), m_TypingModule(TypingModule) { ModifyGestureRecognizer().m_RecognizeTap = true; ModifyGestureRecognizer().m_RecognizeManipulationTranslate = true; UpdateDimensions(); }
template <typename T> MenuWidget<T>::MenuWidget(Vector2n Position, std::vector<T> & Entries, TypingModule * TypingModule) : Widget(Position, Vector2n::ZERO, {}), m_Entries(Entries), m_TypingModule(TypingModule) { ModifyGestureRecognizer().m_RecognizeTap = true; UpdateDimensions(); }
void MultitouchTestBoxWidget::ProcessManipulationBegin(const InputEvent & InputEvent) { const PointerState & PointerState = InputEvent.m_PostEventState; //printf("MultitouchTestBoxWidget::ProcessManipulationBegin()\n"); /*Vector2d PositionDouble = GetParent()->GlobalToCanvas(Vector2n(PointerState.GetAxisState(0).GetPosition(), PointerState.GetAxisState(1).GetPosition())); Vector2n PositionInt(std::lround(PositionDouble.X()), std::lround(PositionDouble.Y())); // TODO: Loss of accuracy? Fix it if needed.*/ auto ParentLocalPosition = GlobalToParent(Vector2n(PointerState.GetAxisState(0).GetPosition(), PointerState.GetAxisState(1).GetPosition())); ModifyGestureRecognizer().m_ManipulationOffset = GetPosition() - ParentLocalPosition; }
template <typename T> ListWidget<T>::ListWidget(Vector2n Position, std::vector<T> & List, TypingModule & TypingModule) #if DECISION_LIST_WIDGET_IS_COMPOSITE : CompositeWidget(Position, { std::shared_ptr<Widget>(new ButtonWidget(Vector2n(-1, -17), Vector2n(lineHeight, lineHeight), [&]() { // TEST: This is specific stuff for quick testing if (!m_Entries.empty()) { m_Entries.pop_back(); } } )) }, {}), #else : Widget(Position, Vector2n::ZERO, {}), #endif m_Entries(List), m_TypingModule(TypingModule) { ModifyGestureRecognizer().m_RecognizeTap = true; UpdateDimensions(); }
GitDiffWidget::GitDiffWidget(Vector2n Position, TypingModule & TypingModule, TextFileWidget * Target) : FlowLayoutWidget(Position, { std::shared_ptr<Widget>(m_SourceWidget = new ConnectionWidget<TextFileWidget>(Vector2n::ZERO, Target)), std::shared_ptr<Widget>(new LabelWidget(Vector2n::ZERO, std::string("git diff"), LabelWidget::Background::Normal)), std::shared_ptr<Widget>(m_OutputWidget = new TextFieldWidget(Vector2n::ZERO, TypingModule)) }, { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }) { m_OutputWidget->m_GetLineHighlighting = GetLineHighlighting(); // TODO: Not quite sure if MinimizeToggle is applicable here, as the content is not really there // (By that, I mean that originally MinimizeToggle was meant to simply not render content which was actually valid, whereas here it's used to hide the output when the content is out of date) m_OutputWidget->m_MinimizeToggle = m_SourceWidget->m_LiveToggle; m_SourceWidget->m_OnChange = [&]() { std::string InOut = ""; if (nullptr != m_SourceWidget->Target()) { std::string Folder = ParsePath(m_SourceWidget->Target()->GetPath(), 0); std::string Filename = ParsePath(m_SourceWidget->Target()->GetPath(), 1); auto Shell = std::unique_ptr<ShellWidget>(new ShellWidget(Vector2n::ZERO, TypingModule)); std::string Command = "cd \'" + Folder + "\'\ngit diff --no-ext-diff -- \'" + Filename + "\'"; //Command += " | tail -n +5"; // Skip the first 4 lines (if any) Shell->m_CommandWidget->SetContent(Command); Shell->m_ExecuteWidget->GetAction()(); InOut = Shell->m_OutputWidget->GetContent(); SkipFirstLines(InOut, 4); TrimLastNewline(InOut); } m_OutputWidget->SetContent(InOut); }; m_SourceWidget->m_OnChange(); ModifyGestureRecognizer().AddShortcut(GestureRecognizer::ShortcutEntry('R', PointerState::Modifiers::Super, m_SourceWidget->m_OnChange, "Run/Refresh")); }
LiveProgramFileWidget::LiveProgramFileWidget(Vector2n Position, std::string Path, TypingModule & TypingModule, Project & Project) : FlowLayoutWidget(Position, { std::shared_ptr<Widget>(m_SourceFileWidget = new TextFileWidget(Vector2n::ZERO, Path, TypingModule)), std::shared_ptr<Widget>(m_ProgramWidget = new ProgramWidget(Vector2n::ZERO, TypingModule, Project, m_SourceFileWidget->m_TextFieldWidget)) }, { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }), m_SourceWidget(m_SourceFileWidget->m_TextFieldWidget) { m_SourceFileWidget->RemoveAllBehaviors(); m_ProgramWidget->RemoveAllBehaviors(); m_SourceWidget->m_GetAutocompletions = GetAutocompletions(m_SourceFileWidget->GetPath()); // Add Dependee connector to the source widget (for connecting text fields that the source widget depends on) for (int i = 0; i < 3; ++i) { auto Dependee = new ConnectionWidget<TextFieldWidget>(Vector2n(-16 - 2, (16 + 2) * i)); // TODO: Should do NotifyExternalChange() as long as I don't expect the contents of this thing to actually depend on the dependee... Dependee->m_OnChange = [=]() { m_SourceWidget->NotifyChange(); }; m_SourceWidget->AddWidget(Dependee); } ModifyGestureRecognizer().AddShortcut(GestureRecognizer::ShortcutEntry('R', PointerState::Modifiers::Super, [=]() { m_SourceWidget->NotifyChange(true); }, "Run/Refresh")); }
SayWidget::SayWidget(Vector2n Position, TypingModule & TypingModule) : FlowLayoutWidget(Position, { std::shared_ptr<Widget>(m_InputWidget = new TextFieldWidget(Vector2n::ZERO, TypingModule)), std::shared_ptr<Widget>(new LabelWidget(Vector2n::ZERO, std::string("say"), LabelWidget::Background::Normal))/*, std::shared_ptr<Widget>(m_ExecuteWidget = new ButtonWidget(Vector2n::ZERO, Vector2n(16, 16), [&](){} ))*/ }, { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }) { // TEST m_InputWidget->SetContent("Shall we play a game?"); { m_ExecuteWidget = new ButtonWidget(Vector2n::ZERO, Vector2n(16, 16), [](){}); m_ExecuteWidget->SetAction([&]() { { auto Pid = fork(); if (0 == Pid) { execl("/usr/bin/say", "/usr/bin/say", m_InputWidget->GetContent().c_str(), (char *)0); // TODO: Add error checking on above execl(), and do exit() in case execution reaches here //exit(1); // Not needed, just in case I comment out the above } else if (-1 == Pid) { std::cerr << "Error forking.\n"; throw 0; } else { } } }); } ModifyGestureRecognizer().AddShortcut(GestureRecognizer::ShortcutEntry('R', PointerState::Modifiers::Super, m_ExecuteWidget->GetAction())); }
void InputManager::RequestTypingPointer(GestureRecognizer & Target) { // DECISION: Only allow pointer change when it's not captured if (nullptr == m_TypingPointer->GetPointerMapping().GetCapturer()) //if (true) { #if 0 m_TypingPointer->ModifyPointerMapping().RemoveAllMappings(); m_TypingPointer->ModifyPointerMapping().AddMapping(Target); m_TypingPointer->ModifyPointerMapping().DoneAdding(); #else m_TypingPointer->ModifyPointerMapping().RemoveAllMappings(); if (nullptr != &Target) // HACK: I should use pointer rather than reference (but too lazy to manually change all invokations...) { m_TypingPointer->ModifyPointerMapping().AddMapping(Target); for (auto Parent = static_cast<Widget *>(&Target.GetOwner())->ModifyParent(); nullptr != Parent; Parent = Parent->ModifyParent()) { m_TypingPointer->ModifyPointerMapping().AddMapping(Parent->ModifyGestureRecognizer()); } } m_TypingPointer->ModifyPointerMapping().DoneAdding(); #endif } }
TextFileWidget::TextFileWidget(Vector2n Position, std::string Path, TypingModule & TypingModule) : FlowLayoutWidget(Position, { std::shared_ptr<Widget>(new FlowLayoutWidget(Vector2n::ZERO, { std::shared_ptr<Widget>(m_FileMinimizeToggle = new ToggleWidget(Vector2n::ZERO, Vector2n(12, 12), [](bool State) { if (!State) g_InputManager->RequestTypingPointer(*static_cast<GestureRecognizer *>(nullptr)); }, true)), std::shared_ptr<Widget>(new LabelWidget(Vector2n(0, -lineHeight - 2), Path, LabelWidget::Background::Normal)) }, {})), std::shared_ptr<Widget>(m_TextFieldWidget = new TextFieldWidget(Vector2n::ZERO, TypingModule)) }, { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }, FlowLayoutWidget::LayoutType::Vertical), m_Path(Path) { m_TextFieldWidget->SetContent(FromFileToString(Path)); m_OnChange = [=]() // Saving takes place in TextFileWidget when it gets its NotifyChange() from the contained TextFieldWidget { //PlayBeep(); //printf("Saving '%s'.\n", Path.c_str()); // Write to file WriteToFile(Path, m_TextFieldWidget->GetContent()); }; const std::string Folder = ParsePath(Path, 0); const std::string Filename = ParsePath(Path, 1); auto CopyPath = [this, &TypingModule]() { #if DECISION_USE_CLIPBOARD_INSTEAD_OF_TypingModule glfwSetClipboardString(this->m_Path); #else TypingModule.SetString(this->m_Path); #endif }; ModifyGestureRecognizer().AddShortcut(GestureRecognizer::ShortcutEntry('I', PointerState::Modifiers::Super, CopyPath, "Copy Path")); // TEST: Line Gutters #if 0 //if ("./Gen/5086673/gistfile1.go" == Path) m_TextFieldWidget->m_GetLineGutters = [=](uint32 LineNumber) -> std::string { #if 0 std::string x = "."; Ls(x); return std::to_string(LineNumber + 1); #endif // HACK: Pass file folder and name info if (0 == LineNumber) return Folder; else if (1 == LineNumber) return Filename; else throw 0; }; #endif if (IsFileTrackedByGit(Path)) { auto GitDiff = new GitDiffWidget(Vector2n::ZERO, TypingModule, this); GitDiff->RemoveAllBehaviors(); AddWidget(GitDiff); auto GitCommit = new ButtonWidget(Vector2n(-160, -350), [=, &TypingModule]() { auto Shell = std::unique_ptr<ShellWidget>(new ShellWidget(Vector2n::ZERO, TypingModule)); std::string Command = "cd \'" + Folder + "\'\ngit commit --allow-empty-message -m '' -- \'" + Filename + "\'"; Command += "\ngit push origin master"; Shell->m_CommandWidget->SetContent(Command); Shell->m_ExecuteWidget->GetAction()(); this->NotifyExternalChange(); // Do this to triger potential GitDiffWidget, GitStatusWidget, etc. //std::cerr << "Commit & Push: '" << Folder << "' folder and '" << Filename << "' file.\n"; std::cerr << Shell->m_OutputWidget->GetContent() << endl; }, "Commit & Push"); AddWidget(GitCommit); } m_TextFieldWidget->m_MinimizeToggle = m_FileMinimizeToggle; }
ProgramWidget::ProgramWidget(Vector2n Position, TypingModule & TypingModule, Project & Project, TextFieldWidget * Target) : FlowLayoutWidget(Position, { std::shared_ptr<Widget>(m_SourceWidget = new ConnectionWidget<TextFieldWidget>(Vector2n::ZERO, Target)), std::shared_ptr<Widget>(m_StdinWidget = new ConnectionWidget<TextFieldWidget>(Vector2n::ZERO)), std::shared_ptr<Widget>(new LabelWidget(Vector2n::ZERO, std::string("go run"), LabelWidget::Background::Normal)), std::shared_ptr<Widget>(m_OutputWidget = new TextFieldWidget(Vector2n::ZERO, TypingModule)) }, { std::shared_ptr<Behavior>(new DraggablePositionBehavior(*this)) }), m_Project(Project) { // TODO: Not quite sure if MinimizeToggle is applicable here, as the content is not really there // (By that, I mean that originally MinimizeToggle was meant to simply not render content which was actually valid, whereas here it's used to hide the output when the content is out of date) m_OutputWidget->m_MinimizeToggle = m_SourceWidget->m_LiveToggle; { m_SourceWidget->m_OnChange = [=, &Project]() { //PlayBeep(); Project.m_ProcessEndedTime = glfwGetTime(); Project.m_BackgroundState = 0; // Kill child processes if (0 != Project.m_LastPid) { //std::cout << "Sending kill to last child pid " << Project.m_LastPid << ".\n"; //auto Result = kill(0, SIGTERM); auto Result = killpg(Project.m_LastPid, SIGKILL); //waitpid(m_LastPid, NULL, 0); if (0 != Result) { std::cerr << "Error: kill() failed with return " << Result << ", errno " << errno << ".\n"; //throw 0; } } //std::cout << "Closing " << Project.m_PipeFd[0] << " and " << Project.m_PipeFd[1] << "; "; close(Project.m_PipeFd[0]); // Close the read end of the pipe in the parent Project.m_PipeFd[0] = Project.m_PipeFd[1] = -1; if (!m_SourceWidget->m_LiveToggle->GetState()) { //OutputWidget.m_Visible = false; return; } else { //OutputWidget.m_Visible = true; } std::string Content = ""; if (nullptr != m_SourceWidget->Target()) { Content = m_SourceWidget->Target()->GetContent(); } Project.GenerateProgram(Content); Project.SetStdinContent(m_StdinWidget); g_OutputWidget = m_OutputWidget; // HACK //m_OutputWidget->SetContent(""); Project.m_ProcessStartedTime = glfwGetTime(); Project.m_ExpiredOutput = true; Project.m_BackgroundState = 1; }; m_StdinWidget->m_OnChange = m_SourceWidget->m_OnChange; // If there's already a Target for source, then refresh ourselves m_SourceWidget->m_OnChange(); } ModifyGestureRecognizer().AddShortcut(GestureRecognizer::ShortcutEntry('R', PointerState::Modifiers::Super, m_SourceWidget->m_OnChange, "Run/Refresh")); m_GetLineAnnotations = [this](uint32 LineNumber) -> std::string { //return std::to_string(LineNumber + 1) + "-" + (m_ProgramWidget->m_OutputWidget->GetContent().length() >= 3 ? m_ProgramWidget->m_OutputWidget->GetContent().substr(0, 3) : "..."); // HACK: Using hardcoded color, make this better // Don't do this if the result is not compile error, or if Live Toggle is off if ( Color(1.0, 0.9, 0.9) != this->m_OutputWidget->GetBackground() || false == this->m_SourceWidget->m_LiveToggle->GetState()) return ""; // TODO: Clean up { std::stringstream ss; auto Input = this->m_OutputWidget->GetContent(); TrimLastNewline(Input); ss << Input; std::string Line; // Skip first line if it starts with "#" if (Line.length() >= 1 && '#' == Line[0]) std::getline(ss, Line); for (;;) { std::getline(ss, Line); // Parse one go error line try { auto FirstColon = Line.find(':'); auto SecondColon = Line.find(':', FirstColon + 1); uint32 FoundLineNumber = std::stoi(Line.substr(FirstColon + 1, SecondColon - (FirstColon + 1))); if (FoundLineNumber == LineNumber + 1) return TrimFirstSpace(Line.substr(SecondColon + 1)); } catch (...) {} if (ss.eof()) break; } } return ""; }; }
FunctionWidget::FunctionWidget(Vector2n Position, Function & Function) : Widget(Position, Vector2n(904, (3 + 2/*f.body_lines.size()*/) * lineHeight), {}), m_Function(Function) { ModifyGestureRecognizer().m_RecognizeTap = true; }
void TextFieldWidget::ProcessTap(InputEvent & InputEvent, Vector2n Position) { g_InputManager->RequestTypingPointer(ModifyGestureRecognizer()); }
void ConceptStringBoxWidget::ProcessTap(const InputEvent & InputEvent, Vector2n Position) { g_InputManager->RequestTypingPointer(ModifyGestureRecognizer()); }