Skip to content
Closed
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
7 changes: 6 additions & 1 deletion framework/rcommand/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ target_sources(muse_rcommand PRIVATE
rcommandmodule.cpp
rcommandmodule.h
icommanddispatcher.h
imodulecommands.h
icommandsregister.h
imodulecommandsregister.h
icommandsstate.h
imodulecommandsstate.h
commandable.h
commandtypes.h

internal/commanddispatcher.cpp
internal/commanddispatcher.h
internal/commandsregister.cpp
internal/commandsregister.h
internal/commandsstate.cpp
internal/commandsstate.h
)

#if (MUSE_MODULE_RCOMMAND_TESTS)
Expand Down
21 changes: 21 additions & 0 deletions framework/rcommand/commandtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ struct CommandInfo
Decoration decoration;
};

struct CommandState {
bool enabled = false;
bool checked = false;

CommandState() = default;
CommandState(bool enabled, bool checked)
: enabled(enabled), checked(checked) {}
CommandState(bool enabled)
: enabled(enabled), checked(false) {}

bool operator==(const CommandState& other) const
{
return enabled == other.enabled && checked == other.checked;
}

bool operator!=(const CommandState& other) const
{
return !this->operator==(other);
}
};

// Call

using CallId = uint64_t;
Expand Down
11 changes: 8 additions & 3 deletions framework/rcommand/icommandsregister.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#pragma once

#include "modularity/imoduleinterface.h"
#include "imodulecommands.h"

#include "imodulecommandsregister.h"
#include "commandtypes.h"

namespace muse::rcommand {
class ICommandsRegister : MODULE_GLOBAL_INTERFACE
Expand All @@ -29,9 +31,12 @@ class ICommandsRegister : MODULE_GLOBAL_INTERFACE
public:
virtual ~ICommandsRegister() = default;

virtual void reg(const IModuleCommandsPtr& module) = 0;
virtual void unreg(const IModuleCommandsPtr& module) = 0;
virtual void reg(const IModuleCommandsRegisterPtr& module) = 0;
virtual void unreg(const IModuleCommandsRegisterPtr& module) = 0;
virtual IModuleCommandsRegisterPtr moduleRegister(const std::string& moduleName) const = 0;

virtual std::vector<CommandInfo> commandList() const = 0;

virtual const std::string& commandModuleName(const Command& command) const = 0;
};
}
43 changes: 43 additions & 0 deletions framework/rcommand/icommandsstate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore/Audacity CLA applies
*
* Copyright (C) MuseScore/Audacity and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "modularity/imoduleinterface.h"

#include "global/async/channel.h"

#include "imodulecommandsstate.h"
#include "commandtypes.h"

namespace muse::rcommand {
class ICommandsState : MODULE_CONTEXT_INTERFACE
{
INTERFACE_ID(ICommandsState);

public:
virtual ~ICommandsState() = default;

virtual void reg(const IModuleCommandsStatePtr& module) = 0;
virtual void unreg(const IModuleCommandsStatePtr& module) = 0;

virtual CommandState commandState(const Command& command) const = 0;
virtual async::Channel<Command, CommandState> commandStateChanged() const = 0;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once
#pragma once

#include <string>
#include <vector>
Expand All @@ -26,15 +26,17 @@
#include "commandtypes.h"

namespace muse::rcommand {
class IModuleCommands
class IModuleCommandsRegister
{
public:

virtual ~IModuleCommands() = default;
virtual ~IModuleCommandsRegister() = default;

virtual std::string moduleName() const = 0;
virtual const std::vector<CommandInfo>& commandInfos() const = 0;

virtual const std::vector<Command>& commandList() const = 0;
virtual const std::vector<CommandInfo>& commandInfoList() const = 0;
};

using IModuleCommandsPtr = std::shared_ptr<IModuleCommands>;
using IModuleCommandsRegisterPtr = std::shared_ptr<IModuleCommandsRegister>;
}
45 changes: 45 additions & 0 deletions framework/rcommand/imodulecommandsstate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore/Audacity CLA applies
*
* Copyright (C) MuseScore/Audacity and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include <string>
#include <memory>

#include "global/async/channel.h"

#include "commandtypes.h"

namespace muse::rcommand {
class IModuleCommandsState
{
public:
virtual ~IModuleCommandsState() = default;

virtual std::string moduleName() const = 0;

virtual void init() = 0;
virtual void deinit() = 0;

virtual CommandState commandState(const Command& command) const = 0;
virtual async::Channel<Command, CommandState> commandStateChanged() const = 0;
};

using IModuleCommandsStatePtr = std::shared_ptr<IModuleCommandsState>;
}
35 changes: 32 additions & 3 deletions framework/rcommand/internal/commandsregister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
using namespace muse;
using namespace muse::rcommand;

void CommandsRegister::reg(const IModuleCommandsPtr& module)
void CommandsRegister::reg(const IModuleCommandsRegisterPtr& module)
{
IF_ASSERT_FAILED(module) {
return;
Expand All @@ -41,9 +41,13 @@ void CommandsRegister::reg(const IModuleCommandsPtr& module)
}

m_modules[moduleName] = module;

for (const auto& info : module->commandInfoList()) {
m_commandModuleNames[info.command] = moduleName;
}
}

void CommandsRegister::unreg(const IModuleCommandsPtr& module)
void CommandsRegister::unreg(const IModuleCommandsRegisterPtr& module)
{
IF_ASSERT_FAILED(module) {
return;
Expand All @@ -55,14 +59,39 @@ void CommandsRegister::unreg(const IModuleCommandsPtr& module)
}

m_modules.erase(moduleName);

for (const auto& info : module->commandInfoList()) {
m_commandModuleNames.erase(info.command);
}
}

IModuleCommandsRegisterPtr CommandsRegister::moduleRegister(const std::string& moduleName) const
{
auto it = m_modules.find(moduleName);
if (it != m_modules.end()) {
return it->second;
}

return nullptr;
}

std::vector<CommandInfo> CommandsRegister::commandList() const
{
std::vector<CommandInfo> commands;
for (const auto& module : m_modules) {
const auto& infos = module.second->commandInfos();
const auto& infos = module.second->commandInfoList();
commands.insert(commands.end(), infos.begin(), infos.end());
}
return commands;
}

const std::string& CommandsRegister::commandModuleName(const Command& command) const
{
auto it = m_commandModuleNames.find(command);
if (it != m_commandModuleNames.end()) {
return it->second;
}

static const std::string empty;
return empty;
}
12 changes: 9 additions & 3 deletions framework/rcommand/internal/commandsregister.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ class CommandsRegister : public ICommandsRegister
public:
CommandsRegister() = default;

void reg(const IModuleCommandsPtr& module) override;
void unreg(const IModuleCommandsPtr& module) override;
void reg(const IModuleCommandsRegisterPtr& module) override;
void unreg(const IModuleCommandsRegisterPtr& module) override;
IModuleCommandsRegisterPtr moduleRegister(const std::string& moduleName) const override;

std::vector<CommandInfo> commandList() const override;

const std::string& commandModuleName(const Command& command) const override;

private:
std::map<std::string, IModuleCommandsPtr> m_modules;
std::map<std::string, IModuleCommandsRegisterPtr> m_modules;

std::map<Command, std::string> m_commandModuleNames;
};
}
102 changes: 102 additions & 0 deletions framework/rcommand/internal/commandsstate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore/Audacity CLA applies
*
* Copyright (C) 2026 MuseScore/Audacity and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "commandsstate.h"

using namespace muse;
using namespace muse::rcommand;

void CommandsState::reg(const IModuleCommandsStatePtr& module)
{
IF_ASSERT_FAILED(module) {
return;
}

const std::string& moduleName = module->moduleName();
IF_ASSERT_FAILED(!moduleName.empty()) {
return;
}

IF_ASSERT_FAILED(m_modules.find(moduleName) == m_modules.end()) {
LOGW() << "module already registered: " << moduleName;
return;
}

m_modules[moduleName] = module;

module->commandStateChanged().onReceive(this, [this](const Command& command, const CommandState& state) {
m_cache[command] = state;
m_commandStateChanged.send(command, state);
});

module->init();
}

void CommandsState::unreg(const IModuleCommandsStatePtr& module)
{
IF_ASSERT_FAILED(module) {
return;
}

const std::string& moduleName = module->moduleName();
IF_ASSERT_FAILED(!moduleName.empty()) {
return;
}

module->commandStateChanged().disconnect(this);
module->deinit();

m_modules.erase(moduleName);
}

async::Channel<Command, CommandState> CommandsState::commandStateChanged() const
{
return m_commandStateChanged;
}

CommandState CommandsState::commandState(const Command& command) const
{
{
auto it = m_cache.find(command);
if (it != m_cache.end()) {
return it->second;
}
}

const std::string& moduleName = commandsRegister()->commandModuleName(command);
IF_ASSERT_FAILED(!moduleName.empty()) {
return CommandState();
}

auto mit = m_modules.find(moduleName);
IF_ASSERT_FAILED(mit != m_modules.end()) {
return CommandState();
}

const IModuleCommandsStatePtr& module = mit->second;
IF_ASSERT_FAILED(module) {
return CommandState();
}

CommandState state = module->commandState(command);

m_cache[command] = state;

return state;
}
Loading