ご対応いただき、ありがとうございます。
IME入力中の分節に関する動作は改善されましたが、問題の文字が含まれたまま確定した場合、
確定文字列中にその文字が含まれるものの、GetKeyInputStringでは除去されて返ってくる都合上、
カーソルの位置に不整合が生じ、これを解決するためにはIME入力中に長さ0の文節の存在を監視し、
確定文字列をパーツに区切って描画する等しなければならないため、素直にUNICODEを使用するべき、という結論に至りました。
この度はお忙しい中対応してくださり、ありがとうございました。
中途半端ですが、やってみた分を貼っておきます
#include "DxLib.h"
#include <string>
#include <sstream>
#include <vector>
#define white (DxLib::GetColor(255, 255, 255))
#define black (DxLib::GetColor(0, 0, 0))
#define red (DxLib::GetColor(255, 0, 0))
#define green (DxLib::GetColor(0, 255, 0))
#define blue (DxLib::GetColor(0, 0, 255))
typedef struct Point_t { // 座標、またはサイズ
Point_t() : x(0), y(0) {}
Point_t(int _x, int _y) : x(_x), y(_y) {}
Point_t operator+(const Point_t& _rhs) const { return Point_t(x + _rhs.x, y + _rhs.y); }
Point_t operator*(int _scale) const { return Point_t(x * _scale, y * _scale); }
Point_t operator/(int _den) const { return Point_t(x / _den, y / _den); }
public:
int x, y;
} Point, Size;
struct Rect { // 矩形
Rect() : lt(), size() {}
Rect(int _l, int _t, int _r, int _b) : lt(_l, _t), size(_r - _l, _b - _t) {}
Rect(const Point& _lt, const Size& _size) : lt(_lt), size(_size) {}
Rect operator+(const Size& _point) const { return Rect(lt + _point, size); }
Rect operator*(int _scale) const { return Rect(lt, size * _scale); }
int r() const { return l + size.x; }
int b() const { return t + size.y; }
public:
Point lt;
Size size;
int& l = lt.x, & t = lt.y;
};
typedef struct Position_t { // 位置情報
int bytes;
int pixels;
} Position, Length;
struct Clause { // 文節情報
Clause(const std::string& _str, const Position& _position, const Length& _length)
: str(_str), position(_position), length(_length) {}
std::string str;
Position position;
Length length;
};
struct Candidate { // 変換文字列情報
Candidate(const std::string& _str, const Length& _length) : str(_str), length(_length) {}
std::string str;
Length length;
};
int MyDrawString(const Point& _p, const std::string& _s, unsigned _color = white, unsigned _edge_color = 0) {
return DxLib::DrawString(_p.x, _p.y, _s.c_str(), _color, _edge_color);
}
int MyDrawStringEX(const Point& _p, float _ext, const std::string& _s, unsigned _color = white, unsigned _edge_color = 0) {
return DxLib::DrawExtendString(_p.x, _p.y, _ext, _ext, _s.c_str(), _color, _edge_color);
}
int MyDrawBox(const Rect& _r, unsigned _color, bool _fill) {
return DxLib::DrawBox(_r.l, _r.t, _r.r(), _r.b(), _color, (int)_fill);
}
int MyDrawStringWidth(const std::string& _s, int _len) {
return DxLib::GetDrawStringWidth(_s.c_str(), _len);
}
VECTOR transducer(int _idx, float _phase) {
constexpr float d = 400.f;
constexpr float r = 800.f;
float z = (float)_idx + _phase;
float sign = (z < 0 ? -1.f : 1.f);
z = std::fabsf(z) * r / 9.5f;
if (z >= r)
return { r * sign, 0.f, 0.f };
float x = std::sqrtf(r * r - (r - z) * (r - z)) * sign;
return { x, 0., d / std::sqrtf(x * x + (d + z) * (d + z)) };
}
/* 文字系設定値 */
constexpr int max_length = 256; // キー入力最大文字数 (byte)
constexpr int font_size = 64; // フォントのサイズ
constexpr int font_thick = 8; // フォントの太さ
constexpr int font_edge = 4; // フォントのエッジの太さ
const std::string font_name = "Meiryo UI"; // フォント名
/* 座標設定値 */
const Size screen_size = { 1280, 800 }; // バックバッファのサイズ
const Point p1 = { 64, 64 }; // 確定文字列描画原点
const Point p2 = screen_size / 2; // 未確定文字列(IME入力中文字列) 描画原点
/* 処理用 */
std::string confirmed_string(max_length, 0); // 確定文字列
std::string unconfirmed_string; // 未確定文字列(IME入力中文字列)
Position cursor_confirmed; // 確定文字列のカーソル位置情報
Position cursor_unconfirmed; // 未確定文字列(IME入力中文字列) のカーソル位置情報
int ki;
int frames = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
DxLib::ChangeWindowMode(true);
DxLib::SetGraphMode(screen_size.x, screen_size.y, 16);
if (DxLib_Init() < 0)
return -1;
DxLib::SetDrawScreen(DX_SCREEN_BACK);
DxLib::SetDefaultFontState(font_name.c_str(), font_size, font_thick, -1, -1, font_edge);
ki = DxLib::MakeKeyInput(max_length, true, false, false);
DxLib::SetActiveKeyInput(ki);
while (0 == ProcessMessage()) {
if (DxLib::CheckKeyInput(ki)) break;
DxLib::ClearDrawScreen();
// 確定文字列に対する処理
DxLib::GetKeyInputString(&confirmed_string[0], ki);
cursor_confirmed.bytes = DxLib::GetKeyInputCursorPosition(ki);
cursor_confirmed.pixels = MyDrawStringWidth(confirmed_string, cursor_confirmed.bytes);
// 確定文字列の描画
MyDrawBox(Rect(p1 + Size(cursor_confirmed.pixels, 0), Size(2, font_size)), red, true);
MyDrawString(p1, confirmed_string, white);
// 未確定文字列(IME入力中の文字列) に対する処理
auto imipd = GetIMEInputData();
if (imipd) {
std::vector<Clause> clauses; // 文節情報
int selected_clause; // 選択中の文節インデックス
std::vector<Candidate> candidates; // 変換候補情報
int selected_candidate; // 選択中の変換候補インデックス
static int prev_selected_clause; // 前フレームの選択文節インデックス
static int prev_selected_candidate; // 前フレームの選択変換候補インデックス
static float clause_phase = 0.f; // 文節のズレ
static float candidate_phase = 0.f; // 変換候補のズレ
unconfirmed_string = imipd->InputString;
cursor_unconfirmed.bytes = imipd->CursorPosition;
cursor_unconfirmed.pixels = MyDrawStringWidth(unconfirmed_string, cursor_unconfirmed.bytes);
// 文節情報を構築
selected_clause = imipd->SelectClause;
for (int i = 0; i < imipd->ClauseNum; ++i) {
Position pos;
Length len;
std::string s;
pos.bytes = imipd->ClauseData[i].Position;
len.bytes = imipd->ClauseData[i].Length;
s = unconfirmed_string.substr(pos.bytes, len.bytes);
pos.pixels = MyDrawStringWidth(unconfirmed_string, pos.bytes);
len.pixels = MyDrawStringWidth(s, (int)s.size());
clauses.emplace_back(s, pos, len);
}
// 変換候補情報を構築
selected_candidate = imipd->SelectCandidate;
for (int i = 0; i < imipd->CandidateNum; ++i) {
std::string s(imipd->CandidateList[i]);
Length len;
len.bytes = (int)s.size();
len.pixels = MyDrawStringWidth(s, (int)s.size());
candidates.emplace_back(s, len);
}
// ズレの表現
if (clause_phase < 0.f) {
clause_phase += 0.1f;
if (0.f <= clause_phase)
clause_phase = 0.f;
}
if (0.f < clause_phase) {
clause_phase -= 0.1f;
if (clause_phase <= 0.f)
clause_phase = 0.f;
}
if (prev_selected_clause < selected_clause)
clause_phase = 1.f;
if (selected_clause < prev_selected_clause)
clause_phase = -1.f;
if (candidate_phase < 0.f) {
candidate_phase += 0.1f;
if (0.f <= candidate_phase)
candidate_phase = 0.f;
}
if (0.f < candidate_phase) {
candidate_phase -= 0.1f;
if (candidate_phase <= 0.f)
candidate_phase = 0.f;
}
if (prev_selected_candidate < selected_candidate)
candidate_phase = 1.f;
if (selected_candidate < prev_selected_candidate)
candidate_phase = -1.f;
// 文節情報を描画
for (int i = 0; i < clauses.size(); ++i) {
if (selected_clause < 0)
break;
VECTOR v = transducer(i - selected_clause, clause_phase);
DxLib::SetDrawBlendMode(DX_BLENDMODE_ALPHA, (int)(255.f * v.z * v.z));
MyDrawStringEX(p2 + Point((int)v.x, (int)((1.f - v.z) * (float)font_size)), v.z, clauses[i].str.c_str(), white);
}
DxLib::SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 255);
if (0 <= selected_clause && selected_clause < clauses.size()) {
Rect r(Point(clauses[selected_clause].position.pixels, 0), Size(clauses[selected_clause].length.pixels + 4, font_size));
MyDrawBox(r + p1 + Size(cursor_confirmed.pixels, 0), blue, true);
}
MyDrawString(p1 + Size(cursor_confirmed.pixels, 0), unconfirmed_string, white);
// 変換候補情報を描画
if (selected_candidate < candidates.size()) {
DxLib::SetDrawBlendMode(DX_BLENDMODE_ALPHA, std::abs(((frames << 2) & 255) - 128) + 128);
Rect r(Point(), Size(candidates[selected_candidate].length.pixels, font_size));
MyDrawBox(r + p2, green, true);
}
for (int i = 0; i < candidates.size(); ++i) {
if (abs(i - selected_candidate) > 8)
continue;
VECTOR v = transducer(i - selected_candidate, candidate_phase);
DxLib::SetDrawBlendMode(DX_BLENDMODE_ALPHA, (int)(255.f * v.z * v.z));
MyDrawStringEX(p2 + Point((int)((1.f - v.z) * .5f * (float)candidates[i].length.pixels), (int)(v.x * 0.5f)), v.z, candidates[i].str.c_str(), white);
}
DxLib::SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 255);
prev_selected_clause = selected_clause;
prev_selected_candidate = selected_candidate;
++frames;
}
DxLib::ScreenFlip();
}
DxLib::DeleteKeyInput(ki);
DxLib::DxLib_End();
return 0;
}