Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Tea Code.app/Contents/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>launcher</string>

<key>CFBundleIdentifier</key>
<string>com.blurrysquire.teacode</string>

<key>CFBundleName</key>
<string>teacode</string>

<key>CFBundleDisplayName</key>
<string>TeaCode</string>

<key>CFBundleVersion</key>
<string>1.0</string>

<key>CFBundleShortVersionString</key>
<string>1.0</string>

<key>CFBundlePackageType</key>
<string>APPL</string>

<key>LSMinimumSystemVersion</key>
<string>14.0</string>

<key>CFBundleIconFile</key>
<string>AppIcon</string>
</dict>
</plist>
7 changes: 7 additions & 0 deletions Tea Code.app/Contents/MacOS/launcher
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# Tea Code launcher

#set current dir
cd "$(dirname "$0")"
./tea-code
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tea Code.app/Contents/Resources/AppIcon.icns
Binary file not shown.
33 changes: 33 additions & 0 deletions package/macos/info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>launcher</string>

<key>CFBundleIdentifier</key>
<string>com.blurrysquire.teacode</string>

<key>CFBundleName</key>
<string>teacode</string>

<key>CFBundleDisplayName</key>
<string>TeaCode</string>

<key>CFBundleVersion</key>
<string>1.0</string>

<key>CFBundleShortVersionString</key>
<string>1.0</string>

<key>CFBundlePackageType</key>
<string>APPL</string>

<key>LSMinimumSystemVersion</key>
<string>14.0</string>

<key>CFBundleIconFile</key>
<string>AppIcon</string>
</dict>
</plist>
7 changes: 7 additions & 0 deletions package/macos/launcher
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# Tea Code launcher

#set current dir
cd "$(dirname "$0")"
./tea-code
29 changes: 29 additions & 0 deletions scripts/make_macosbundle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"

APP_NAME="Tea Code"
APP_BUNDLE="$ROOT_DIR/${APP_NAME}.app"

rm -rf "$APP_BUNDLE" #if it exists

mkdir -p "$APP_BUNDLE/Contents/"
mkdir -p "$APP_BUNDLE/Contents/MacOS"
mkdir -p "$APP_BUNDLE/Contents/MacOS/resources"
mkdir -p "$APP_BUNDLE/Contents/Resources"

cp "$ROOT_DIR/tea-code" "$APP_BUNDLE/Contents/MacOS/tea-code"
cp "$ROOT_DIR/package/macos/launcher" "$APP_BUNDLE/Contents/MacOS/launcher"

chmod +x "$APP_BUNDLE/Contents/MacOS/tea-code"
chmod +x "$APP_BUNDLE/Contents/MacOS/launcher"

cp "$ROOT_DIR/package/macos/settings.toml" "$APP_BUNDLE/Contents/MacOS/settings.toml"
cp "$ROOT_DIR/branding/AppIcon.icns" "$APP_BUNDLE/Contents/Resources/AppIcon.icns"
cp "$ROOT_DIR/package/macos/info.plist" "$APP_BUNDLE/Contents/Info.plist"
cp "$ROOT_DIR/resources/modified-icon.png" "$APP_BUNDLE/Contents/MacOS/resources/modified-icon.png"

echo "Created $APP_BUNDLE"
17 changes: 14 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum {
#include "settings.hpp"
#include "file_view.hpp"
#include "editor_tabs.hpp"
#include "terminal.hpp"

class MainFrame : public wxFrame {
public:
Expand Down Expand Up @@ -48,10 +49,16 @@ class MainFrame : public wxFrame {
if (!startup_path.empty()) {
this->file_view->PopulateTree(startup_path);
}
this->workspace_path = startup_path;
this->editor_splitter = new wxSplitterWindow(this->splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE);
this->editor_tabs = new EditorTabs(this->editor_splitter, wxID_ANY);
this->terminal = new Terminal(this->editor_splitter, wxID_ANY, this->workspace_path);

this->editor_tabs = new EditorTabs(this->splitter, wxID_ANY);
this->editor_splitter->SplitHorizontally(this->editor_tabs, this->terminal, -200);
this->editor_splitter->SetMinimumPaneSize(50);
this->editor_splitter->SetSashGravity(1.0);

this->splitter->SplitVertically(this->file_view, this->editor_tabs, 200);
this->splitter->SplitVertically(this->file_view, this->editor_splitter, 200);
this->splitter->SetMinimumPaneSize(50);

Bind(wxEVT_MENU, &MainFrame::OnNewFile, this, wxID_NEW);
Expand All @@ -74,9 +81,11 @@ class MainFrame : public wxFrame {

private:
wxSplitterWindow* splitter;
wxSplitterWindow* editor_splitter;
wxString workspace_path;
FileView* file_view;
EditorTabs* editor_tabs;
Terminal* terminal;

void OnNewFile(wxCommandEvent& event) {
this->editor_tabs->NewFile();
Expand All @@ -100,7 +109,9 @@ class MainFrame : public wxFrame {

if (dir_dialog.ShowModal() == wxID_CANCEL) return;

this->file_view->PopulateTree(dir_dialog.GetPath());
this->workspace_path = dir_dialog.GetPath();
this->file_view->PopulateTree(this->workspace_path);
this->terminal->ChangeDirectory(this->workspace_path);
}

void OnFileViewActivated(wxCommandEvent& event) {
Expand Down
219 changes: 219 additions & 0 deletions src/terminal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#include "terminal.hpp"

#include <wx/sizer.h>
#include <wx/utils.h>

//TODO:
// input is only after the prompt
// use ptx instead of this emulation

Terminal::Terminal(wxWindow* parent, wxWindowID id, const wxString& working_directory)
: wxPanel(parent, id), process(nullptr), pid(0), input_start(0),
working_directory(working_directory), applying_programmatic_change(false), poll_timer(this)
{
this->console = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_RICH2 | wxBORDER_NONE);

// read from settings
wxFont mono_font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
this->console->SetFont(mono_font);

wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(this->console, 1, wxEXPAND);
this->SetSizer(sizer);

this->console->Bind(wxEVT_CHAR, &Terminal::OnChar, this);
this->console->Bind(wxEVT_LEFT_UP, &Terminal::OnConsoleClick, this);
this->console->Bind(wxEVT_TEXT, &Terminal::OnTextChanged, this);
Bind(wxEVT_TIMER, &Terminal::OnPollTimer, this, this->poll_timer.GetId());
Bind(wxEVT_END_PROCESS, &Terminal::OnProcessTerminated, this);

this->StartShell();

this->console->SetFocus();
}

Terminal::~Terminal() {
this->poll_timer.Stop();

if (this->process != nullptr) {
this->process->Detach();
wxKill(this->pid, wxSIGTERM);
}
}

void Terminal::StartShell() {
#ifdef __WXMSW__
wxString shell_command = "cmd.exe";
#else
wxString shell_command = "/bin/bash -i";
#endif

wxEnvVariableHashMap env;
wxGetEnvMap(&env);
env["TERM"] = "dumb";
env["PS1"] = "\\w $ ";

this->process = new wxProcess(this);
this->process->Redirect();

wxExecuteEnv execute_env;
execute_env.env = env;
if (!this->working_directory.empty()) {
execute_env.cwd = this->working_directory;
}

this->pid = wxExecute(shell_command, wxEXEC_ASYNC, this->process, &execute_env);

if (this->pid == 0) {
delete this->process;
this->process = nullptr;
this->WriteLine("failed to start shell");
return;
}

//this->SendCommand("stty -echo");

this->poll_timer.Start(100);
}

void Terminal::SendCommand(const wxString& command) {
if (this->process == nullptr) return;

wxOutputStream* out_stream = this->process-> GetOutputStream();
if (out_stream != nullptr) {
wxString line = command + "\n";

out_stream->Write(line.mb_str(), line.length());
}
}

void Terminal::ChangeDirectory(const wxString& path) {
this->working_directory = path;
this->SendCommand("cd " + path);
}

void Terminal::MarkLocked() {
this->input_start = this->console->GetLastPosition();
this->locked_prefix = this->console->GetValue();
}

void Terminal::OnChar(wxKeyEvent& event) {
long selection_start, selection_end;
this->console->GetSelection(&selection_start, &selection_end);

bool crosses_boundary = selection_start != selection_end && selection_start < this->input_start;

if (crosses_boundary) {
this->console->SetInsertionPointEnd();

if (event.GetKeyCode() != WXK_RETURN && event.GetKeyCode() != WXK_BACK && event.GetKeyCode() != WXK_DELETE) {
event.Skip();
}

return;
}

long caret = this->console->GetInsertionPoint();

if (event.GetKeyCode() == WXK_RETURN) {
wxString command = this->console-> GetRange(this->input_start, this->console->GetLastPosition());
this->console->AppendText("\n");
this->MarkLocked();

this->SendCommand(command);

return;
}

if ((event.GetKeyCode() == WXK_BACK || event.GetKeyCode() == WXK_DELETE) && caret <= this->input_start) {
return;
}

if (caret < this->input_start) {
this->console->SetInsertionPointEnd();
}

event.Skip();
}

void Terminal::OnConsoleClick(wxMouseEvent& event) {
event.Skip();
this->CallAfter(&Terminal::ClampInsertionPoint);
}

void Terminal::ClampInsertionPoint() {
if (this->console->GetInsertionPoint() < this->input_start) {
this->console->SetInsertionPointEnd();
}
}

void Terminal::OnTextChanged(wxCommandEvent& event) {
event.Skip();

if (this->applying_programmatic_change) return;

wxString current = this->console->GetValue();

bool prefix_intact = (long)current.length() >= this->input_start && current.Left(this->input_start) == this->locked_prefix;

if (!prefix_intact) {
this->applying_programmatic_change = true;
this->console->ChangeValue(this->locked_prefix);
this->console->SetInsertionPointEnd();
this->applying_programmatic_change = false;
}
}

void Terminal::ReadStream() {
if (this->process == nullptr) return;
wxInputStream* in_stream = this->process->GetInputStream();
wxInputStream* err_stream = this->process->GetErrorStream();

if (in_stream != nullptr) {
while (this->process->IsInputAvailable()) {
char buffer[4096];
in_stream->Read(buffer, sizeof(buffer )- 1);

size_t read_count = in_stream->LastRead();

buffer[read_count] = '\0';

if (read_count > 0) {
this->console-> AppendText(wxString(buffer, wxConvUTF8));
this->MarkLocked();
}
}
}
if (err_stream != nullptr) {
while (this->process->IsErrorAvailable()) {
char buffer[4096];
err_stream->Read(buffer, sizeof(buffer )- 1);

size_t read_count = err_stream->LastRead();

buffer[read_count] = '\0';

if (read_count > 0) {
this->console-> AppendText(wxString(buffer, wxConvUTF8));
this->MarkLocked();
}
}
}
}

void Terminal::OnPollTimer(wxTimerEvent& event) {
this->ReadStream();
}

void Terminal::OnProcessTerminated(wxProcessEvent& event) {
this->ReadStream();

this->poll_timer.Stop();
//this->WriteLine("ProcessTerminated");
this->process = nullptr;
}

void Terminal::WriteLine(const wxString& text) {
this->console->AppendText(text + "\n");
this->MarkLocked();
}
Loading