Exemple #1
0
double OOPLyric::GetLineProgress() const {
    LineInfo *currLine = *m_currLine;
    size_t lineHasGone = m_stopWatch->Time() - currLine->GetStartTime();
    if (lineHasGone >= currLine->GetMilSeconds()) {
        return 1;
    }

    // 假如除数为 0,不会来到这里
    return double(lineHasGone) / currLine->GetMilSeconds();
}
Exemple #2
0
void OOPLyric::OnMouseEvent(VdkMouseEvent &e) {
    switch (e.evtCode) {
    case RIGHT_UP: {
        // 拖动歌词时不要响应右键事件
        if (TestState(OLST_DRAGGING)) {
            return;
        }

        wxPoint menuPos(AbsoluteRect().GetPosition());
        menuPos.x += e.mousePos.x;
        menuPos.y += e.mousePos.y;

        m_Window->ShowContextMenu(this, menuPos);

        break;
    }

    case LEFT_DOWN: {
        m_draggDistance = e.mousePos.y;
        SetAddinState(OLST_DRAGGING_STARTED);

        break;
    }

    case DRAGGING: {
        // 不接受先在一首歌的会话中拖动歌词,然后在未释放鼠标的情况下
        // 另一首歌开始播放,继续前面的拖动事件
        if (!TestState(OLST_DRAGGING_STARTED)) {
            break;
        }

        if (!IsOk()) {
            break;
        }

        if (m_timer.IsRunning()) {
            Pause();
            SetAddinState(OLST_RUNNING_BEFORE_DRAGGING);
        }

        SetAddinState(OLST_DRAGGING);

        //======================================================

        int ystart;
        GetViewStartCoord(NULL, &ystart);

        int dY = e.mousePos.y - m_draggDistance;
        m_draggDistance = e.mousePos.y;

        // 这是一行我们手工加上去的空行
        int rowHeight = GetRowHeight();
        int upperBound =
            (*(m_parser->begin()))->GetLyric().empty() ? rowHeight : 0;

        // 无法继续将帘布向上卷(再拖下去就到下一首了)
        // 我们将拖到尽头的事件视为无效
        bool lastLine = false;

        // 情景:将虚拟画布像窗帘一样向下拖
        // 拖动尽头了,不能再把窗帘哪怕拖下一寸
        if (ystart - dY < upperBound) {
            dY = ystart - upperBound; // 加加减减的原因参照(*)
        } else {
            int maxy;
            GetMaxViewStartCoord(NULL, &maxy);

            // 将帘布向上卷,卷到尽头了,再卷下去就会导致
            // 无法完整遮住窗口
            if (ystart - dY > maxy) {
                dY = ystart - maxy; // 加加减减的原因参照(*)
                // 无效拖动事件
                lastLine = true;
            }
        }

        if (dY) {
            SetViewStart(0, ystart - dY, &e.dc);   //………………(*)
        }

        // 无效拖动事件
        if (lastLine) {
            m_draggHit = m_parser->end();
        }

        //===================================================
        // 绘制中间线段

        wxRect rc(GetAbsoluteRect());
        int y = rc.y + m_blankLinesTop * GetRowHeight();

        m_Window->ResetDcOrigin(e.dc);
        e.dc.SetPen(wxPen(m_TextColor));
        e.dc.DrawLine(rc.x, y, rc.GetRight(), y);

        break;
    }

    case NORMAL:
    case LEFT_UP: {
        if (!TestState(OLST_DRAGGING) ||
            !TestState(OLST_DRAGGING_STARTED)) {
            break;
        }

        wxASSERT(IsOk());

        //-----------------------------------------------------

        RemoveState(OLST_DRAGGING | OLST_DRAGGING_STARTED);

        // 拖到最下面了,尽头
        if (m_draggHit == m_parser->end()) {
            RefreshState(&e.dc);
        } else {
            int ystart; // 起始绘图坐标
            GetViewStartCoord(NULL, &ystart);

            LineInfo *lineDraggHit = *m_draggHit;
            size_t timeToGo = lineDraggHit->GetStartTime();

            int rowHeight = GetRowHeight();
            double linePercentage = double(ystart % rowHeight) / rowHeight ;
            timeToGo += lineDraggHit->GetMilSeconds() * linePercentage;

            // 歌词可能并不匹配正在播放的歌曲
            if (timeToGo < m_parser->GetTimeSum()) {
                m_currLine = m_draggHit;

                if (IsReadyForEvent()) {
                    FireEvent(&e.dc, (void *) timeToGo);
                }
            }
        }

        bool resume = TestState(OLST_RUNNING_BEFORE_DRAGGING);
        if (resume) {
            RemoveState(OLST_RUNNING_BEFORE_DRAGGING);
            Resume();
        }

        break;
    }

    default:

        break;
    }
}
Exemple #3
0
VdkCusdrawReturnFlag OOPLyric::DoDrawCellText
        (const VdkLcCell *cell,
         int col_index,
         int index0,
         wxDC &dc,
         VdkLcHilightState state) {
    wxASSERT(m_parser);

    // 注意:index 是不计算加入的空行的
    int index = (int) (cell->GetClientData()) - 1;
    if (index == -1) { // 此时 ClientData == NULL,是我们添加的空行
        return VCCDRF_DODEFAULT;
    }

    dc.SetTextForeground(m_TextColor);

    // 暂停时高亮当前行,情景见于用户正在拖动歌词。
    // 另外先暂停,然后拖动歌词完毕,此时假如歌词秀是以卡拉OK
    // 方式进行显示时,那么不会保持半高亮的状态,而是全高亮。
    if (TestState(OLST_PAUSED)) {
        int yStart;
        GetViewStartCoord(NULL, &yStart);

        int rowHeight = GetRowHeight();
        int dragRegion = yStart + rowHeight * m_blankLinesTop;
        int index2 = index + m_blankLinesTop;

        // 检测拖动歌词时中间线下面的一行
        if (rowHeight * index2 <= dragRegion &&
            rowHeight * (index2 + 1) > dragRegion) {
            m_draggHit = m_parser->GetLine(index);

            dc.SetTextForeground(m_HilightColor);
        }

        return VCCDRF_DODEFAULT;
    }

    LineInfo *currLine = *m_currLine;
    // TODO: 是否考虑优化?
    size_t currLineIndex = m_parser->IndexOf(m_currLine);
    int lineHasGone = m_stopWatch->Time() - currLine->GetStartTime();

    if ((index == currLineIndex - 1) && !cell->GetLabel().empty()) {
        // 使用渐变色还原上一句歌词
        if (lineHasGone < ALPHA_SHOW_LAST_LINE_MS) {
            unsigned char r, g, b;
            double alpha2 = double(lineHasGone) / ALPHA_SHOW_LAST_LINE_MS;
            double alpha1 = 1 - alpha2;

            r = m_HilightColor.Red() * alpha1 + m_TextColor.Red() * alpha2;
            g = m_HilightColor.Green() * alpha1 + m_TextColor.Green() * alpha2;
            b = m_HilightColor.Blue() * alpha1 + m_TextColor.Blue() * alpha2;

            dc.SetTextForeground(wxColour(r, g, b));
        }
    } else if (index == currLineIndex) { // 高亮当前文本行
        // 尽管这是一种很罕见的情况,但一旦出现了就会导致下面 (*) 表达式
        // 的除数为 0
        if (currLine->GetMilSeconds() == 0) {
            return VCCDRF_DODEFAULT;
        }

        if (!cell->IsEmpty()) {
            /* 经验教训:
            1. SetClippingRegiion 有叠加效应,因此在执行新的
               SetClippingRegiion 前别忘了销毁原来的 ClippingRegiion 。
            2. 关于表达式中整数与浮点数混用:注意中间运算结果会
               被强制转换成 int 然后参加下一步的运算,并不是对
               最终结果进行转换,使之成为一个浮点数。
            */

            const int rowHeight = GetRowHeight();
            int y = (currLineIndex + m_blankLinesTop) * rowHeight;
            cell->DrawLabel(dc, 0, y);

            // (*)
            double lineProgress = double(lineHasGone) / currLine->GetMilSeconds();

            // 要实现 KALA-OK 效果的文本宽度
            int w = (m_Rect.width - cell->GetX_Padding() * 2) * lineProgress;

            wxRect rc(GetAbsoluteRect());
            const int bottom = rc.y + rc.height;

            int yStart;
            VdkScrolledWindow::GetViewStartCoord(NULL, &yStart);
            rc.y += y - yStart;

            rc.width = cell->GetX_Padding() + w;
            rc.height = rowHeight;
            // 不能使 KALA-OK 效果的 ClippingRegion 超出列表窗口
            if ((rc.y + rc.height) > bottom) {
                rc.height = bottom - rc.y;
            }

            VdkDcDeviceOriginSaver saver(dc);
            dc.SetDeviceOrigin(0, 0);
            VdkDcClippingRegionDestroyer destroyer(dc, rc);

#       ifdef __WXGTK__
            dc.SetBrush(m_crossBrush1);
            dc.DrawRectangle(rc);
#       endif
            dc.SetTextForeground(m_HilightColor);

            cell->DrawLabel(dc, rc.x, rc.y);
        }

        return VCCDRF_SKIPDEFAULT;
    }

    return VCCDRF_DODEFAULT;
}