Skip to content

Commit

Permalink
Added additional keyboard controls so that links can be followed with…
Browse files Browse the repository at this point in the history
…out needing a mouse. Updated README
  • Loading branch information
jhhoward committed May 26, 2021
1 parent 0471577 commit 9d0ee84
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 32 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ To run you will need:
* Intel 8088 or compatible CPU
* CGA compatible graphics card (EGA and VGA are backwards compatible)
* A network interface (it is possible to use your machine's serial port with the EtherSLIP driver)
* A mouse
* A mouse is desirable but not 100% required

## Limitations
* Text only (this may change in a later release)
* HTTP only (no HTTPS support)
* No CSS or Javascript
* Very long pages may be truncated if there is not enough RAM available

## Keyboard shortcuts
Escape : Exit
F2 : Invert screen (useful for LCD displays)
F6 / Ctrl+L : Select address bar
Tab / Shift+Tab : Cycle through selectable page elements
Backspace : Back in history

The page position can be scrolled with cursor keys, Page up, Page down, Home and End

## Network setup
MicroWeb uses Michael Brutman's [mTCP networking library](http://www.brutman.com/mTCP/) for the network stack. You will need a DOS packet driver relevant to your network interface. You can read more about configuring DOS networking [here](http://www.brutman.com/Dos_Networking/dos_networking.html)

Expand Down
2 changes: 1 addition & 1 deletion src/DOS/CGA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ bool CGADriver::ApplyScissor(int& y, int& height)
}
if (y + height >= scissorY2)
{
height = scissorY2 - y - 1;
height = scissorY2 - y;
}
return true;
}
Expand Down
222 changes: 204 additions & 18 deletions src/Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,20 @@ AppInterface::AppInterface(App& inApp) : app(inApp)
oldMouseY = -1;
oldButtons = 0;
textFieldCursorPosition = 0;
clickingButton = false;
}

void AppInterface::Reset()
{
oldPageHeight = 0;
if (activeWidget && !activeWidget->isInterfaceWidget)
{
activeWidget = NULL;
}
if (hoverWidget && !hoverWidget->isInterfaceWidget)
{
hoverWidget = NULL;
}
}

void AppInterface::DrawInterfaceWidgets()
Expand Down Expand Up @@ -163,16 +172,25 @@ void AppInterface::Update()
case KEYCODE_BACKSPACE:
app.PreviousPage();
break;
case KEYCODE_CTRL_I:
case KEYCODE_F2:
{
Platform::input->HideMouse();
Platform::video->InvertScreen();
Platform::input->ShowMouse();
}
break;
case KEYCODE_CTRL_L:
case KEYCODE_F6:
ActivateWidget(&addressBar);
break;

case KEYCODE_TAB:
CycleWidgets(1);
break;
case KEYCODE_SHIFT_TAB:
CycleWidgets(-1);
break;

default:
// printf("%x\n", keyPress);
break;
Expand Down Expand Up @@ -243,6 +261,19 @@ void AppInterface::DeactivateWidget()
{
if (activeWidget)
{
if (activeWidget->type == Widget::Button)
{
if (clickingButton)
{
app.renderer.InvertWidget(activeWidget);
}
else
{
Widget* widget = activeWidget;
activeWidget = NULL;
app.renderer.RedrawWidget(widget);
}
}
if (activeWidget->type == Widget::TextField)
{
if (textFieldCursorPosition == -1)
Expand All @@ -254,6 +285,10 @@ void AppInterface::DeactivateWidget()
app.renderer.DrawTextFieldCursor(activeWidget, textFieldCursorPosition, true);
}
}
else if (activeWidget->GetLinkURL())
{
InvertWidgetsWithLinkURL(activeWidget->GetLinkURL());
}
activeWidget = NULL;
}
}
Expand All @@ -272,8 +307,14 @@ void AppInterface::ActivateWidget(Widget* widget)
switch (activeWidget->type)
{
case Widget::Button:
activeWidget = hoverWidget;
app.renderer.InvertWidget(activeWidget);
if (clickingButton)
{
app.renderer.InvertWidget(activeWidget);
}
else
{
app.renderer.RedrawWidget(activeWidget);
}
break;
case Widget::TextField:
if (activeWidget == &addressBar && strlen(activeWidget->textField->buffer) > 0)
Expand All @@ -287,9 +328,31 @@ void AppInterface::ActivateWidget(Widget* widget)
app.renderer.DrawTextFieldCursor(activeWidget, textFieldCursorPosition, false);
}
break;
default:
if (widget->GetLinkURL())
{
InvertWidgetsWithLinkURL(widget->GetLinkURL());
app.renderer.SetStatus(URL::GenerateFromRelative(app.page.pageURL.url, widget->GetLinkURL()).url);
}
break;
}
}
}

void AppInterface::InvertWidgetsWithLinkURL(const char* url)
{
for (int n = app.renderer.GetPageTopWidgetIndex(); n < app.page.numFinishedWidgets; n++)
{
Widget* pageWidget = &app.page.widgets[n];
if (pageWidget->y > app.renderer.GetScrollPosition() + Platform::video->windowHeight)
{
break;
}
if (pageWidget->GetLinkURL() == url)
{
app.renderer.InvertWidget(pageWidget);
}
}
}

void AppInterface::HandleClick(int mouseX, int mouseY)
Expand Down Expand Up @@ -339,6 +402,7 @@ void AppInterface::HandleClick(int mouseX, int mouseY)
}
else if (hoverWidget->type == Widget::Button)
{
clickingButton = true;
ActivateWidget(hoverWidget);
}
else if (hoverWidget->type == Widget::ScrollBar)
Expand All @@ -364,28 +428,40 @@ void AppInterface::HandleClick(int mouseX, int mouseY)
}
}

void AppInterface::HandleButtonClicked(Widget* widget)
{
if(widget->type == Widget::Button)
{
if (widget->button->form)
{
SubmitForm(widget->button->form);
}
else if (widget == &backButton)
{
app.PreviousPage();
}
else if (widget == &forwardButton)
{
app.NextPage();
}
}
}

void AppInterface::HandleRelease()
{
if (activeWidget && activeWidget->type == Widget::Button)
{
app.renderer.InvertWidget(activeWidget);

if (hoverWidget == activeWidget)
if (clickingButton)
{
if (activeWidget->button->form)
{
SubmitForm(activeWidget->button->form);
}
else if (activeWidget == &backButton)
if (hoverWidget == activeWidget)
{
app.PreviousPage();
}
else if (activeWidget == &forwardButton)
{
app.NextPage();
HandleButtonClicked(activeWidget);
}
DeactivateWidget();

activeWidget = NULL;
clickingButton = false;
}
activeWidget = NULL;
}
else if (activeWidget == &scrollBar)
{
Expand Down Expand Up @@ -553,7 +629,22 @@ bool AppInterface::HandleActiveWidget(InputButtonCode keyPress)
}
break;
case Widget::Button:
return true;
if (clickingButton)
{
return true;
}
if (keyPress == KEYCODE_ENTER)
{
HandleButtonClicked(activeWidget);
}
break;
default:
if (keyPress == KEYCODE_ENTER && activeWidget->GetLinkURL())
{
app.OpenURL(URL::GenerateFromRelative(app.page.pageURL.url, activeWidget->GetLinkURL()).url);
return true;
}
break;
}

return false;
Expand Down Expand Up @@ -633,3 +724,98 @@ void AppInterface::UpdatePageScrollBar()

app.renderer.RedrawScrollBar();
}

void AppInterface::CycleWidgets(int direction)
{
if (app.page.numFinishedWidgets == 0)
{
return;
}

int pageScrollBottom = app.renderer.GetScrollPosition() + Platform::video->windowHeight;

// If there is an active widget, find index and check if it is on screen
int currentPos = -1;
if (activeWidget && !activeWidget->isInterfaceWidget)
{
for (int n = 0; n < app.page.numFinishedWidgets; n++)
{
Widget* widget = &app.page.widgets[n];
if (activeWidget == widget)
{
if (app.renderer.GetScrollPosition() < widget->y + widget->height && pageScrollBottom > widget->y)
{
currentPos = n;
}
break;
}
}
}

// If there is no active widget or not on screen, find the first widget on screen
if (currentPos == -1)
{
if (direction > 0)
{
for (currentPos = app.renderer.GetPageTopWidgetIndex(); currentPos < app.page.numFinishedWidgets; currentPos++)
{
Widget* widget = &app.page.widgets[currentPos];
if (widget->y >= app.renderer.GetScrollPosition())
{
break;
}
}

if (currentPos == app.page.numFinishedWidgets)
{
currentPos = 0;
}
}
else
{
for (currentPos = app.page.numFinishedWidgets - 1; currentPos >= 0; currentPos--)
{
Widget* widget = &app.page.widgets[currentPos];
if (widget->y + widget->height < pageScrollBottom)
{
break;
}
}

if (currentPos < 0)
{
currentPos = app.page.numFinishedWidgets - 1;
}
}
}

int startPos = currentPos;

while (1)
{
Widget* widget = &app.page.widgets[currentPos];
if (widget != activeWidget)
{
if ((widget->type == Widget::TextField) || (widget->type == Widget::Button) || (widget->GetLinkURL() && widget->GetLinkURL() != activeWidget->GetLinkURL()))
{
ActivateWidget(widget);
app.renderer.ScrollTo(widget);
return;
}
}

currentPos += direction;
if (currentPos >= app.page.numFinishedWidgets)
{
currentPos = 0;
}
if (currentPos < 0)
{
currentPos = app.page.numFinishedWidgets - 1;
}
if (currentPos == startPos)
{
break;
}
}
}
5 changes: 5 additions & 0 deletions src/Interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AppInterface

void UpdatePageScrollBar();
Widget* GetActiveWidget() { return activeWidget; }
int GetTextFieldCursorPosition() { return textFieldCursorPosition; }

Widget scrollBar;
Widget addressBar;
Expand All @@ -50,6 +51,9 @@ class AppInterface
void HandleRelease();
bool HandleActiveWidget(InputButtonCode keyPress);
void SubmitForm(WidgetFormData* form);
void CycleWidgets(int direction);
void InvertWidgetsWithLinkURL(const char* url);
void HandleButtonClicked(Widget* widget);

void ActivateWidget(Widget* widget);
void DeactivateWidget();
Expand All @@ -66,6 +70,7 @@ class AppInterface
int scrollBarRelativeClickPositionY;
int oldPageHeight;
int textFieldCursorPosition;
bool clickingButton;

ButtonWidgetData backButtonData;
ButtonWidgetData forwardButtonData;
Expand Down
15 changes: 14 additions & 1 deletion src/KeyCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,18 @@
#define KEYCODE_BACKSPACE 8
#define KEYCODE_ENTER 13

#define KEYCODE_CTRL_I 9
#define KEYCODE_TAB 0x0009
#define KEYCODE_SHIFT_TAB 0x0f00

#define KEYCODE_CTRL_L 12

#define KEYCODE_F1 0x3b00
#define KEYCODE_F2 0x3c00
#define KEYCODE_F3 0x3d00
#define KEYCODE_F4 0x3e00
#define KEYCODE_F5 0x3f00
#define KEYCODE_F6 0x4000
#define KEYCODE_F7 0x4100
#define KEYCODE_F8 0x4200
#define KEYCODE_F9 0x4300
#define KEYCODE_F10 0x4400
Loading

0 comments on commit 9d0ee84

Please sign in to comment.