Basic mouse handling support!

This commit is contained in:
casey langen 2018-05-27 23:46:31 -07:00
parent f9d57c06b4
commit 9048467e45
37 changed files with 468 additions and 101 deletions

View File

@ -1959,6 +1959,7 @@ static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
{
int button_down = -1, button_up = -1;
static int mouse_buttons_pressed = 0;
static int mouse_click_type = -1;
static LPARAM mouse_lParam;
static uint64_t last_click_time[PDC_MAX_MOUSE_BUTTONS];
/* in millisec since 1970 */
@ -2128,9 +2129,15 @@ static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
BUTTON4_PRESSED, BUTTON5_PRESSED };
modified_key_to_return = 0;
if( SP && (SP->_trap_mbe & remap_table[wParam]))
set_mouse( (const int) wParam, BUTTON_PRESSED, mouse_lParam);
if (SP && (SP->_trap_mbe & remap_table[wParam]))
{
if ( mouse_click_type != -1)
{
set_mouse( (const int)wParam, mouse_click_type, mouse_lParam);
}
}
KillTimer( PDC_hWnd, (int)wParam);
mouse_click_type = -1;
mouse_buttons_pressed ^= (1 << wParam);
}
else if( SP && curscr && curscr->_y)
@ -2195,27 +2202,35 @@ static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
if( button_down >= 0)
{
modified_key_to_return = 0;
SetTimer( hwnd, button_down, SP->mouse_wait, NULL);
mouse_buttons_pressed |= (1 << button_down);
mouse_lParam = lParam;
}
if( button_up >= 0)
{
int message_to_send = -1;
static const int single_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_CLICKED, BUTTON2_CLICKED, BUTTON3_CLICKED,
BUTTON4_CLICKED, BUTTON5_CLICKED };
static const int double_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_DOUBLE_CLICKED, BUTTON2_DOUBLE_CLICKED,
BUTTON3_DOUBLE_CLICKED, BUTTON4_DOUBLE_CLICKED,
BUTTON5_DOUBLE_CLICKED };
static const int triple_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_TRIPLE_CLICKED, BUTTON2_TRIPLE_CLICKED,
BUTTON3_TRIPLE_CLICKED, BUTTON4_TRIPLE_CLICKED,
BUTTON5_TRIPLE_CLICKED };
static const int released_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_RELEASED, BUTTON2_RELEASED, BUTTON3_RELEASED,
BUTTON4_RELEASED, BUTTON5_RELEASED };
modified_key_to_return = 0;
if( (mouse_buttons_pressed >> button_up) & 1)
{
const uint64_t curr_click_time =
milliseconds_since_1970( );
static const int double_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_DOUBLE_CLICKED, BUTTON2_DOUBLE_CLICKED,
BUTTON3_DOUBLE_CLICKED, BUTTON4_DOUBLE_CLICKED,
BUTTON5_DOUBLE_CLICKED };
static const int triple_remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_TRIPLE_CLICKED, BUTTON2_TRIPLE_CLICKED,
BUTTON3_TRIPLE_CLICKED, BUTTON4_TRIPLE_CLICKED,
BUTTON5_TRIPLE_CLICKED };
static int n_previous_clicks;
if( curr_click_time <
@ -2224,36 +2239,27 @@ static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
else /* zero for a "normal" click, 1 */
n_previous_clicks = 0; /* for a dblclick, 2 for a triple */
if( n_previous_clicks >= 2 &&
(SP->_trap_mbe & triple_remap_table[button_up]))
message_to_send = BUTTON_TRIPLE_CLICKED;
else if( n_previous_clicks >= 1 &&
(SP->_trap_mbe & double_remap_table[button_up]))
message_to_send = BUTTON_DOUBLE_CLICKED;
else /* either it's not a doubleclick, or we aren't */
{ /* checking for double clicks */
static const int remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_CLICKED, BUTTON2_CLICKED, BUTTON3_CLICKED,
BUTTON4_CLICKED, BUTTON5_CLICKED };
if (n_previous_clicks >= 2 &&
(SP->_trap_mbe & triple_remap_table[button_up]))
mouse_click_type = BUTTON_TRIPLE_CLICKED;
else if (n_previous_clicks >= 1 &&
(SP->_trap_mbe & double_remap_table[button_up]))
mouse_click_type = BUTTON_DOUBLE_CLICKED;
else if (SP->_trap_mbe & single_remap_table[button_up])
/* either it's not a doubleclick, or we aren't */
/* checking for double clicks */
mouse_click_type = BUTTON_CLICKED;
else
mouse_click_type = -1;
if( SP->_trap_mbe & remap_table[button_up])
message_to_send = BUTTON_CLICKED;
}
KillTimer( hwnd, button_up);
SetTimer( hwnd, button_up, SP->mouse_wait, NULL);
mouse_buttons_pressed ^= (1 << button_up);
last_click_time[button_up] = curr_click_time;
}
if( message_to_send == -1) /* might just send as a 'released' msg */
{
static const int remap_table[PDC_MAX_MOUSE_BUTTONS] =
{ BUTTON1_RELEASED, BUTTON2_RELEASED, BUTTON3_RELEASED,
BUTTON4_RELEASED, BUTTON5_RELEASED };
if( SP->_trap_mbe & remap_table[button_up])
message_to_send = BUTTON_RELEASED;
}
if( message_to_send != -1)
set_mouse( button_up, message_to_send, lParam);
if( SP->_trap_mbe & released_remap_table[button_up])
set_mouse(button_up, BUTTON_RELEASED, lParam);
}
return DefWindowProc( hwnd, message, wParam, lParam) ;

View File

@ -39,20 +39,20 @@
namespace musik { namespace core {
extern std::string GetHomeDirectory();
extern std::string GetApplicationDirectory();
extern std::string GetDataDirectory(bool create = true);
extern std::string GetPath(const std::string &sFile);
extern std::string GetPluginDirectory();
extern std::string NormalizeDir(std::string path);
extern void OpenFile(const std::string& path);
extern int64_t Checksum(char *data,unsigned int bytes);
extern size_t CopyString(const std::string& src, char* dst, size_t size);
extern void ReplaceAll(std::string& input, const std::string& find, const std::string& replace);
extern bool FileToByteArray(const std::string& path, char** target, int& size, bool nullTerminate = false);
std::string GetHomeDirectory();
std::string GetApplicationDirectory();
std::string GetDataDirectory(bool create = true);
std::string GetPath(const std::string &sFile);
std::string GetPluginDirectory();
std::string NormalizeDir(std::string path);
void OpenFile(const std::string& path);
int64_t Checksum(char *data,unsigned int bytes);
size_t CopyString(const std::string& src, char* dst, size_t size);
void ReplaceAll(std::string& input, const std::string& find, const std::string& replace);
bool FileToByteArray(const std::string& path, char** target, int& size, bool nullTerminate = false);
/* renames ~/.mC2 -> ~/.musikcube */
extern void MigrateOldDataDirectory();
extern void RemoveOldDlls();
/* file-migration stuff. */
void MigrateOldDataDirectory(); /* renames ~/.mC2 -> ~/.musikcube */
void RemoveOldDlls();
} }

View File

@ -178,7 +178,7 @@ namespace musik { namespace core { namespace lastfm {
});
}
const std::string CreateLinkUrl(const std::string& token) {
const std::string CreateAccountLinkUrl(const std::string& token) {
return ACCOUNT_LINK_URL_BASE + token;
}

View File

@ -45,12 +45,12 @@ namespace musik { namespace core { namespace lastfm {
using TokenCallback = std::function<void(std::string)>;
using SessionCallback = std::function<void(Session)>;
extern void CreateAccountLinkToken(TokenCallback callback);
extern const std::string CreateLinkUrl(const std::string& token);
extern void CreateSession(const std::string& token, SessionCallback session);
extern void Scrobble(musik::core::TrackPtr track);
void CreateAccountLinkToken(TokenCallback callback);
const std::string CreateAccountLinkUrl(const std::string& token);
void CreateSession(const std::string& token, SessionCallback session);
void Scrobble(musik::core::TrackPtr track);
extern Session LoadSession();
extern void SaveSession(const Session& session);
extern void ClearSession();
Session LoadSession();
void SaveSession(const Session& session);
void ClearSession();
} } }

View File

@ -35,6 +35,7 @@ set (CUBE_SRCS
./cursespp/Colors.cpp
./cursespp/DialogOverlay.cpp
./cursespp/InputOverlay.cpp
./cursespp/IMouseHandler.cpp
./cursespp/LayoutBase.cpp
./cursespp/ListOverlay.cpp
./cursespp/ListWindow.cpp

View File

@ -60,6 +60,14 @@ namespace components = musik::core::prefs::components;
x == this->artists || \
x == this->genres
#define CREATE_CATEGORY(view, title, type, order) \
view.reset(new CategoryListView(playback, this->library, type)); \
view->EntryActivated.connect(this, &CategorySearchLayout::OnCategoryEntryActivated); \
view->SetFrameTitle(title); \
view->SetAllowArrowKeyPropagation(); \
this->AddWindow(view); \
view->SetFocusOrder(order);
CategorySearchLayout::CategorySearchLayout(musik::core::audio::PlaybackService& playback, ILibraryPtr library)
: LayoutBase() {
this->library = library;
@ -101,13 +109,6 @@ void CategorySearchLayout::OnLayout() {
this->genres->MoveAndResize(categoryWidth * 2, categoryY, lastCategoryWidth, categoryHeight);
}
#define CREATE_CATEGORY(view, title, type, order) \
view.reset(new CategoryListView(playback, this->library, type)); \
view->SetFrameTitle(title); \
view->SetAllowArrowKeyPropagation(); \
this->AddWindow(view); \
view->SetFocusOrder(order);
void CategorySearchLayout::InitializeWindows(musik::core::audio::PlaybackService& playback) {
this->input.reset(new cursespp::TextInput());
this->input->TextChanged.connect(this, &CategorySearchLayout::OnInputChanged);
@ -158,23 +159,22 @@ void CategorySearchLayout::OnVisibilityChanged(bool visible) {
}
}
void CategorySearchLayout::OnCategoryEntryActivated(
cursespp::ListWindow* listWindow, size_t index)
{
CategoryListView* category =
static_cast<CategoryListView*>(listWindow);
if ((int) index >= 0) {
this->SearchResultSelected(
this,
category->GetFieldName(),
category->GetSelectedId());
}
}
bool CategorySearchLayout::KeyPress(const std::string& key) {
IWindowPtr focus = this->GetFocus();
CategoryListView* category = dynamic_cast<CategoryListView*>(focus.get());
if (category) {
if (key == "KEY_ENTER") {
int index = (int) category->GetSelectedIndex();
if (index >= 0) {
this->SearchResultSelected(
this,
category->GetFieldName(),
category->GetSelectedId());
}
return true;
}
}
if (Hotkeys::Is(Hotkeys::Down, key)) {
if (this->GetFocus() == this->input) {

View File

@ -78,6 +78,7 @@ namespace musik {
private:
void InitializeWindows(musik::core::audio::PlaybackService& playback);
void OnCategoryEntryActivated(cursespp::ListWindow* sender, size_t index);
void Requery();
void SaveSession();

View File

@ -128,7 +128,7 @@ void LastFmOverlay::UpdateMessage() {
case State::WaitingForUser:
case State::RegisterError: {
std::string url = lastfm::CreateLinkUrl(this->linkToken);
std::string url = lastfm::CreateAccountLinkUrl(this->linkToken);
core::ReplaceAll(message, "{{link}}", url);
break;
}
@ -180,7 +180,7 @@ void LastFmOverlay::UpdateButtons() {
this->AddButton(
"o", "o", "open url",
[this](std::string key) {
core::OpenFile(lastfm::CreateLinkUrl(this->linkToken));
core::OpenFile(lastfm::CreateAccountLinkUrl(this->linkToken));
});

View File

@ -152,6 +152,10 @@ void ServerOverlay::InitViews() {
this->shortcuts->AddShortcut("ESC", _TSTR("button_cancel"));
this->shortcuts->AddShortcut("M-s", _TSTR("button_save"));
this->shortcuts->SetChangedCallback([this](std::string key) {
this->KeyPress(key);
});
/* web socket server */
this->enableWssCb.reset(new Checkbox());
this->enableWssCb->SetText(_TSTR("settings_server_enable_websockets"));
@ -323,7 +327,7 @@ bool ServerOverlay::Save() {
}
bool ServerOverlay::KeyPress(const std::string& key) {
if (key == "^[") { /* esc closes */
if (key == "^[" || key == "ESC") { /* esc closes */
this->Dismiss();
return true;
}

View File

@ -212,22 +212,23 @@ void TrackListView::ProcessMessage(IMessage &message) {
}
}
void TrackListView::OnEntryActivated(size_t index) {
if (headers.HeaderAt(this->GetSelectedIndex())) {
TrackPtr track = this->GetSelectedTrack();
PlayQueueOverlays::ShowAlbumDividerOverlay(
MessageQueue(), this->playback, this->library, track);
}
else {
playback::Play(*this, this->playback);
}
ListWindow::OnEntryActivated(index);
}
bool TrackListView::KeyPress(const std::string& key) {
bool handled = false;
if (key == "KEY_ENTER") {
if (headers.HeaderAt(this->GetSelectedIndex())) {
TrackPtr track = this->GetSelectedTrack();
PlayQueueOverlays::ShowAlbumDividerOverlay(
MessageQueue(), this->playback, this->library, track);
}
else {
playback::Play(*this, this->playback);
}
handled = true;
}
else if (Hotkeys::Is(Hotkeys::ContextMenu, key)) {
if (Hotkeys::Is(Hotkeys::ContextMenu, key)) {
TrackPtr track = this->GetSelectedTrack();
if (!headers.HeaderAt(this->GetSelectedIndex())) {
if (track) {

View File

@ -97,6 +97,8 @@ namespace musik {
protected:
virtual cursespp::IScrollAdapter& GetScrollAdapter();
virtual void OnEntryActivated(size_t index);
void OnQueryCompleted(musik::core::db::IQuery* query);
/* this view has headers and track entry types */
@ -151,6 +153,7 @@ namespace musik {
};
void OnTrackChanged(size_t index, musik::core::TrackPtr track);
void ScrollToPlaying();
void SelectFirstTrack();

View File

@ -88,6 +88,7 @@ App::App(const std::string& title) {
this->quit = false;
this->minWidth = this->minHeight = 0;
this->mouseEnabled = true;
#ifdef WIN32
this->iconId = 0;
@ -111,6 +112,7 @@ App::App(const std::string& title) {
keypad(stdscr, TRUE);
refresh();
curs_set(0);
mousemask(ALL_MOUSE_EVENTS, nullptr);
#ifndef WIN32
set_escdelay(20);
@ -228,6 +230,10 @@ void App::Quit() {
this->quit = true;
}
void App::SetMouseEnabled(bool enabled) {
this->mouseEnabled = enabled;
}
#ifdef WIN32
bool App::Running(const std::string& uniqueId) {
return App::Running(uniqueId, uniqueId);
@ -258,6 +264,7 @@ void App::Run(ILayoutPtr layout) {
Colors::SetTheme(this->colorTheme);
}
MEVENT mouseEvent;
int64_t ch;
std::string kn;
@ -312,6 +319,21 @@ process:
else if (kn == "KEY_RESIZE") {
resizeAt = App::Now() + REDRAW_DEBOUNCE_MS;
}
else if (this->mouseEnabled && kn == "KEY_MOUSE") {
#ifdef WIN32
if (nc_getmouse(&mouseEvent) == 0) {
#else
if (getmouse(&mouseEvent) == 0) {
#endif
auto active = this->state.ActiveLayout();
if (active) {
using Event = IMouseHandler::Event;
auto window = dynamic_cast<IWindow*>(active.get());
Event event(mouseEvent, window);
active->MouseEvent(event);
}
}
}
/* order: focused input, global key handler, then layout. */
else if (!this->state.input ||
!this->state.focused->IsVisible() ||

View File

@ -60,6 +60,7 @@ namespace cursespp {
void SetColorMode(Colors::Mode mode);
void SetColorTheme(const std::string& fn);
void SetMinimumSize(int width, int height);
void SetMouseEnabled(bool enabled);
bool IsOverlayVisible() { return this->state.overlay != nullptr; }
void SetMinimizeToTray(bool minimizeToTray);
void Minimize();
@ -123,6 +124,7 @@ namespace cursespp {
Colors::Mode colorMode;
std::string colorTheme;
int minWidth, minHeight;
bool mouseEnabled;
bool quit;
#ifdef WIN32

View File

@ -105,3 +105,12 @@ bool Checkbox::KeyPress(const std::string& key) {
}
return false;
}
bool Checkbox::MouseEvent(const IMouseHandler::Event& event) {
if (event.Button1Clicked()) {
this->FocusInParent();
this->SetChecked(!this->checked);
return true;
}
return false;
}

View File

@ -59,6 +59,7 @@ namespace cursespp {
virtual std::string GetText() { return this->buffer; }
virtual bool IsChecked() { return this->checked; }
virtual bool KeyPress(const std::string& key);
virtual bool MouseEvent(const IMouseHandler::Event& event);
virtual void OnRedraw();
private:

View File

@ -53,6 +53,11 @@ DialogOverlay::DialogOverlay() {
this->shortcuts.reset(new ShortcutsWindow());
this->shortcuts->SetAlignment(text::AlignRight);
this->shortcuts->SetChangedCallback([this](std::string key) {
this->ProcessKey(key);
});
this->LayoutBase::AddWindow(this->shortcuts);
}
@ -116,7 +121,8 @@ DialogOverlay& DialogOverlay::AddButton(
ButtonCallback callback)
{
this->shortcuts->AddShortcut(key, caption);
this->buttons[rawKey] = callback;
this->buttons[rawKey] = callback; /* for KeyPress() */
this->buttons[key] = callback; /* for ShortcutsWindow::ChangedCallback */
this->Layout();
this->Invalidate();
return *this;
@ -127,7 +133,7 @@ DialogOverlay& DialogOverlay::OnDismiss(DismissCallback dismissCb) {
return *this;
}
bool DialogOverlay::KeyPress(const std::string& key) {
bool DialogOverlay::ProcessKey(const std::string& key) {
auto it = this->buttons.find(key);
if (it != this->buttons.end()) {
@ -144,6 +150,13 @@ bool DialogOverlay::KeyPress(const std::string& key) {
return true;
}
return false;
}
bool DialogOverlay::KeyPress(const std::string& key) {
if (this->ProcessKey(key)) {
return true;
}
return LayoutBase::KeyPress(key);
}

View File

@ -79,6 +79,7 @@ namespace cursespp {
private:
void Redraw();
void RecalculateSize();
bool ProcessKey(const std::string& key);
std::string title;
std::string message;

View File

@ -37,10 +37,17 @@
#include "IWindowGroup.h"
#include "IDisplayable.h"
#include "IKeyHandler.h"
#include "IMouseHandler.h"
#include <memory>
namespace cursespp {
class ILayout : public IWindowGroup, public IKeyHandler, public IOrderable, public IDisplayable {
class ILayout:
public IWindowGroup,
public IKeyHandler,
public IMouseHandler,
public IOrderable,
public IDisplayable
{
public:
enum FocusMode {
FocusModeCircular = 0,

View File

@ -0,0 +1,73 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <stdafx.h>
#include "IMouseHandler.h"
#include "IWindow.h"
using namespace cursespp;
IMouseHandler::Event::Event(const Event& original, int childX, int childY) {
x = original.x - childX;
y = original.y - childY;
state = original.state;
}
IMouseHandler::Event::Event(const Event& original, IWindow* parent) {
if (parent) {
int frameOffset = parent->IsFrameVisible() ? 1 : 0;
x = original.x - parent->GetX() - frameOffset;
y = original.y - parent->GetY() - frameOffset;
}
else {
x = original.x;
y = original.y;
}
state = original.state;
}
IMouseHandler::Event::Event(const MEVENT& original, IWindow* parent) {
if (parent) {
int frameOffset = parent->IsFrameVisible() ? 1 : 0;
x = original.x - parent->GetX() - frameOffset;
y = original.y - parent->GetY() - frameOffset;
}
else {
x = original.x;
y = original.y;
}
state = original.bstate;
}

View File

@ -0,0 +1,65 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <stdafx.h>
#include "curses_config.h"
namespace cursespp {
class IWindow;
class IMouseHandler {
public:
struct Event {
Event(const Event& original, int childX, int childY);
Event(const Event& original, IWindow* parent = nullptr);
Event(const MEVENT& original, IWindow* parent = nullptr);
bool Button1Clicked() const { return state & BUTTON1_CLICKED; }
bool Button2Clicked() const { return state & BUTTON2_CLICKED; }
bool Button3Clicked() const { return state & BUTTON3_CLICKED; }
bool Button1DoubleClicked() const { return state & BUTTON1_DOUBLE_CLICKED; }
bool Button2DoubleClicked() const { return state & BUTTON2_DOUBLE_CLICKED; }
bool Button3DoubleClicked() const { return state & BUTTON3_DOUBLE_CLICKED; }
int x, y;
mmask_t state;
};
virtual ~IMouseHandler() { }
virtual bool MouseEvent(const Event& mouseEvent) = 0;
};
}

View File

@ -37,6 +37,7 @@
#include "curses_config.h"
#include "IDisplayable.h"
#include "IOrderable.h"
#include "IMouseHandler.h"
#include <core/runtime/IMessage.h>
#include <core/runtime/IMessageTarget.h>
@ -45,6 +46,7 @@ namespace cursespp {
class IWindow :
public IOrderable,
public IDisplayable,
public IMouseHandler,
public musik::core::runtime::IMessageTarget
{
public:

View File

@ -403,5 +403,23 @@ bool LayoutBase::KeyPress(const std::string& key) {
return true;
}
return false;
}
bool LayoutBase::MouseEvent(const IMouseHandler::Event& mouseEvent) {
for (auto window : this->children) {
auto x = window->GetX();
auto y = window->GetY();
auto cx = window->GetWidth();
auto cy = window->GetHeight();
if (mouseEvent.x >= x && mouseEvent.x < x + cx &&
mouseEvent.y >= y && mouseEvent.y < y + cy)
{
auto relative = IMouseHandler::Event(mouseEvent, window.get());
if (window->MouseEvent(relative)) {
return true;
}
}
}
return false;
}

View File

@ -95,6 +95,9 @@ namespace cursespp {
/* IKeyHandler */
virtual bool KeyPress(const std::string& key);
/* IMouseHandler */
virtual bool MouseEvent(const IMouseHandler::Event& mouseEvent);
/* IWindowGroup */
virtual bool AddWindow(IWindowPtr window);
virtual bool RemoveWindow(IWindowPtr window);

View File

@ -287,6 +287,42 @@ void ListWindow::OnDimensionsChanged() {
this->ScrollTo(this->GetScrollPosition().firstVisibleEntryIndex);
}
bool ListWindow::KeyPress(const std::string& key) {
if (key == "KEY_ENTER") {
auto selected = this->GetSelectedIndex();
if (selected != NO_SELECTION) {
this->OnEntryActivated(selected);
}
}
return ScrollableWindow::KeyPress(key);
}
bool ListWindow::MouseEvent(const IMouseHandler::Event& event) {
bool result = ScrollableWindow::MouseEvent(event);
auto first = this->scrollPosition.firstVisibleEntryIndex;
if (first == NO_SELECTION) {
return result;
}
size_t offset = first + (size_t) event.y;
if (event.Button1Clicked()) {
this->SetSelectedIndex(offset);
}
else if (event.Button1DoubleClicked()) {
this->SetSelectedIndex(offset);
this->OnEntryActivated(offset); /* internal */
}
return result;
}
void ListWindow::OnEntryActivated(size_t index) {
this->EntryActivated(this, index); /* external */
}
IScrollAdapter::ScrollPosition& ListWindow::GetMutableScrollPosition() {
this->scrollPosition.logicalIndex = this->GetSelectedIndex(); /* hack */
return this->scrollPosition;

View File

@ -51,6 +51,7 @@ namespace cursespp {
sigslot::signal3<ListWindow*, size_t, size_t> SelectionChanged;
sigslot::signal2<ListWindow*, size_t> Invalidated;
sigslot::signal2<ListWindow*, size_t> EntryActivated;
ListWindow(std::shared_ptr<IScrollAdapter> adapter, IWindow *parent = nullptr);
ListWindow(IWindow *parent = nullptr);
@ -73,10 +74,14 @@ namespace cursespp {
virtual const IScrollAdapter::ScrollPosition& GetScrollPosition();
virtual bool KeyPress(const std::string& key);
virtual bool MouseEvent(const IMouseHandler::Event& event);
void SetScrollbarVisible(bool visible);
protected:
virtual void OnSelectionChanged(size_t newIndex, size_t oldIndex);
virtual void OnEntryActivated(size_t index);
virtual void OnInvalidated();
virtual void OnDimensionsChanged();
virtual void DecorateFrame();

View File

@ -142,6 +142,15 @@ bool ScrollableWindow::KeyPress(const std::string& key) {
return false;
}
bool ScrollableWindow::MouseEvent(const IMouseHandler::Event& event) {
if (event.Button1Clicked()) {
this->FocusInParent();
return true;
}
return false;
}
void ScrollableWindow::OnRedraw() {
IScrollAdapter *adapter = &GetScrollAdapter();
ScrollPos &pos = this->GetMutableScrollPosition();

View File

@ -64,6 +64,7 @@ namespace cursespp {
virtual void OnDimensionsChanged();
virtual bool KeyPress(const std::string& key);
virtual bool MouseEvent(const IMouseHandler::Event& event);
virtual void ScrollToTop();
virtual void ScrollToBottom();

View File

@ -147,6 +147,28 @@ bool ShortcutsWindow::KeyPress(const std::string& key) {
return false;
}
bool ShortcutsWindow::MouseEvent(const IMouseHandler::Event& mouseEvent) {
if (mouseEvent.Button1Clicked()) {
for (auto entry : this->entries) {
auto& pos = entry->position;
if (mouseEvent.x >= pos.offset &&
mouseEvent.x < pos.offset + pos.width)
{
this->activeKey = entry->key;
this->Redraw();
if (this->changedCallback) {
this->changedCallback(this->activeKey);
}
return true;
}
}
}
return false;
}
void ShortcutsWindow::OnFocusChanged(bool focused) {
if (focused) {
this->originalKey = this->activeKey;
@ -180,6 +202,7 @@ void ShortcutsWindow::OnRedraw() {
size_t leftPadding = this->CalculateLeftPadding();
wmove(c, 0, leftPadding);
size_t currentX = leftPadding;
size_t remaining = this->GetContentWidth();
for (size_t i = 0; i < this->entries.size() && remaining > 0; i++) {
auto e = this->entries[i];
@ -197,6 +220,14 @@ void ShortcutsWindow::OnRedraw() {
std::string key = " " + e->key + " ";
std::string value = " " + e->description + " ";
/* calculate the offset and width, this is used for mouse
click handling! */
size_t width = u8cols(key + value);
e->position.offset = currentX;
e->position.width = width;
currentX += 1 + width; /* 1 is the extra leading space */
/* draw the shortcut key */
size_t len = u8cols(key);
if (len > remaining) {
key = text::Ellipsize(key, remaining);
@ -213,6 +244,7 @@ void ShortcutsWindow::OnRedraw() {
continue;
}
/* draw the description */
len = u8cols(value);
if (len > remaining) {
value = text::Ellipsize(value, remaining);
@ -220,7 +252,6 @@ void ShortcutsWindow::OnRedraw() {
}
checked_wprintw(c, value.c_str());
remaining -= len;
}
}

View File

@ -65,6 +65,7 @@ namespace cursespp {
void SetActive(const std::string& key);
virtual bool KeyPress(const std::string& key) override;
virtual bool MouseEvent(const IMouseHandler::Event& mouseEvent) override;
protected:
virtual void OnRedraw() override;
@ -74,6 +75,10 @@ namespace cursespp {
size_t CalculateLeftPadding();
int getActiveIndex();
struct Position {
int offset{ 0 }, width{ 0 };
};
struct Entry {
Entry(const std::string& key, const std::string& desc, int64_t attrs = -1) {
this->key = key;
@ -81,6 +86,7 @@ namespace cursespp {
this->attrs = attrs;
}
Position position;
std::string key;
std::string description;
int64_t attrs;

View File

@ -259,4 +259,13 @@ void TextInput::SetText(const std::string& value) {
void TextInput::SetHint(const std::string& hint) {
this->hintText = hint;
this->Redraw();
}
bool TextInput::MouseEvent(const IMouseHandler::Event& event) {
if (event.Button1Clicked()) {
this->position = std::max(0, std::min((int)this->bufferLength, event.x));
this->FocusInParent();
return true;
}
return false;
}

View File

@ -72,6 +72,7 @@ namespace cursespp {
virtual InputMode GetInputMode() { return this->inputMode; }
virtual bool KeyPress(const std::string& key);
virtual bool MouseEvent(const IMouseHandler::Event& event);
virtual void SetText(const std::string& value);
virtual std::string GetText() { return this->buffer; }

View File

@ -37,6 +37,7 @@
#include <cursespp/Screen.h>
#include <cursespp/Colors.h>
#include <cursespp/Text.h>
#include <cursespp/ILayout.h>
#include "TextLabel.h"
@ -116,4 +117,13 @@ bool TextLabel::KeyPress(const std::string& key) {
}
return false;
}
}
bool TextLabel::MouseEvent(const IMouseHandler::Event& event) {
if (event.Button1Clicked()) {
this->FocusInParent();
this->Activated(this);
return true;
}
return false;
}

View File

@ -68,6 +68,7 @@ namespace cursespp {
virtual void OnRedraw();
virtual bool KeyPress(const std::string& key);
virtual bool MouseEvent(const IMouseHandler::Event& event);
private:
std::string buffer;

View File

@ -36,6 +36,7 @@
#include "Window.h"
#include "IWindowGroup.h"
#include "IInput.h"
#include "ILayout.h"
#include "Colors.h"
#include "Screen.h"
#include "Text.h"
@ -822,6 +823,18 @@ void Window::SetNavigationKeys(std::shared_ptr<INavigationKeys> keys) {
::keys = keys;
}
bool Window::MouseEvent(const IMouseHandler::Event& mouseEvent) {
return false;
}
bool Window::FocusInParent() {
auto layout = dynamic_cast<ILayout*>(this->GetParent());
if (layout) {
return layout->SetFocus(shared_from_this());
}
return false;
}
/* default keys for navigating around sub-views. apps can override this shim to
provide VIM-like keybindings, if it wants... */
static class DefaultNavigationKeys : public INavigationKeys {

View File

@ -119,6 +119,8 @@ namespace cursespp {
bool HasBadBounds() { return this->badBounds; }
bool MouseEvent(const IMouseHandler::Event& mouseEvent);
static bool WriteToScreen(IInput* input);
static void InvalidateScreen();
static void Freeze();
@ -134,11 +136,14 @@ namespace cursespp {
void PostMessage(int messageType, int64_t user1 = 0, int64_t user2 = 0, int64_t delay = 0);
void DebounceMessage(int messageType, int64_t user1 = 0, int64_t user2 = 0, int64_t delay = 0);
void RemoveMessage(int messageType);
bool FocusInParent();
static INavigationKeys& NavigationKeys();
virtual void Create();
virtual void Destroy();
virtual void DecorateFrame();
void Recreate();
void Clear();
void DrawFrameAndTitle();

View File

@ -177,6 +177,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClCompile Include="cursespp\Checkbox.cpp" />
<ClCompile Include="cursespp\Colors.cpp" />
<ClCompile Include="cursespp\DialogOverlay.cpp" />
<ClCompile Include="cursespp\IMouseHandler.cpp" />
<ClCompile Include="cursespp\InputOverlay.cpp" />
<ClCompile Include="cursespp\LayoutBase.cpp" />
<ClCompile Include="cursespp\ListOverlay.cpp" />
@ -243,6 +244,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
<ClInclude Include="cursespp\IInput.h" />
<ClInclude Include="cursespp\IKeyHandler.h" />
<ClInclude Include="cursespp\ILayout.h" />
<ClInclude Include="cursespp\IMouseHandler.h" />
<ClInclude Include="cursespp\INavigationKeys.h" />
<ClInclude Include="cursespp\InputOverlay.h" />
<ClInclude Include="cursespp\IOrderable.h" />

View File

@ -159,6 +159,9 @@
<ClCompile Include="app\overlay\LastFmOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
<ClCompile Include="cursespp\IMouseHandler.cpp">
<Filter>cursespp</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -367,6 +370,9 @@
<ClInclude Include="app\overlay\LastFmOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
<ClInclude Include="cursespp\IMouseHandler.h">
<Filter>cursespp</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">